Django Postgresql ArrayField aggregation - django

In my Django application, using Postgresql, I have a model with an ArrayField of CharFields.
I would like to know if there's a DB way to aggregate and get a list of all the strings in the table. For example:
['dog', 'cat']
['dog']
['cat']
would yield ['dog', 'cat']
I know how to do that in Python but would like to find out a way to aggregate this on the DB level.
Using Django 1.8.4

In PostgreSQL you can do the following:
SELECT DISTINCT UNNEST(array_column) FROM the_table;
So if your model looks something like
class TheModel(models.Model):
# ...
array_field = ArrayField(models.CharField(max_length=255, blank=True),\
default=list)
# ...
the Django equivalent is:
from django.db.models import Func, F
TheModel.objects.annotate(arr_els=Func(F('array_field'), function='unnest'))\
.values_list('arr_els', flat=True).distinct()

Related

Django query JSONField where the value is equal to {}

I have a model with a JSONField like this:
metadata = JSONField(
_('Metadata'),
blank=True,
default=dict,
help_text=_('Multi key-value field to hold any additional information'),
)
Is there any way in Django to query for objects where metadata is equal to {}? I use Django 2.1.
Simply try this
MyModel.objects.filter(metadata={})
Reference: Querying JSONField in Django

Custom django field type, with modified column look up in SELECT part

I would like to implement a custom Django field type. Namely a TranslatedField that is backed by native json/jsonb using postgres.
But I would also like it to not have to return all translations for a particular query. Something like this:
class TranslatedField(JSONField):
pass
class MyModel(models.Model):
name = TranslatedField()
And then used in an example:
>>> MyModel.objects.create(name={'en': 'Duck', 'fr': 'Canard'})
>>> set_language('fr') # Imagine this function sets a global variable
>>> en_fr = MyModel.objects.get(id=1)
>>> en_fr.name
>>> 'Canard'
Which is fine, I can do that by returning the whole 'name' json from the database. But I would prefer if django issued actual SQL like this that did the lookup in postgres and saved me some bytes across the network:
SELECT 'name'->'fr' FROM 'mymodel';
Does Django have any hooks that let me dynamically change the 'SELECT' part of the query?

How to implement JsonField in django that has postgresql as backend?

I want to implement JsonField in my django application running postgresql.Can I also have indexing on that Json Field so that I can have Mongo like features? Do I have to make use of sqlalchemy for this or django built-in ORM is suitable for this purpose?
Thanks.
You can easily install django-jsonfield
pip install jsonfield
and use it on your field
class MyModel(models.Model):
my_json_field = JSONField()
It's just a TextField that serializes the json object to a python dictionary so no you can't have an index on it nor can you make queries against your json field.
If you are using Postgres (and don't care about compatibility with other DB engines) you should consider django's postgres fields
https://docs.djangoproject.com/en/1.9/ref/contrib/postgres/fields/
this should have much better performance than the ordinary jsonfield.
If DB compatibility is an issue and/or you want your field be readable/editable through django admin you might want to consider KeyValueField instead https://github.com/rewardz/django_model_helpers
Data is stored in DB like this
Name = abc
Age = 123
but returned to you like this
{"Name": "abc", "Age": "123"}
So if you make db_index = True you can do field__contains="Age = 123" but despite using db_index, its not fool proof because Age=1234 will also be returned by that query plus indexing text field is not usually recommended

Django queryset to return first of each item in foreign key based on date

need to get a queryset with the first book (by a date field) for each author (related to by foreign key) ...is there a Django ORM way to do this (without custom SQL preferred but acceptable)
*Edit: Please note that an answer that works using only a modern open source backend like Postgresql is acceptable ..still ORM based solution preferred over pure custom sql query)
Models
class Book(Model):
date = Datefield()
author = ForeignKey(Author)
class Author(Model):
name = CharField()
Book.objects.filter(??)
If you use PostgreSQL or another DB backend with support for DISTINCT ON there is a nice solution:
Books.objects.order_by('author', '-date').distinct('author')
Otherwise I don't know a solution with only one query. But you can try this:
from django.db.models import Q, Max
import operator
books = Book.objects.values('author_id').annotate(max_date=Max('date'))
filters = reduce(operator.or_, [(Q(author_id=b['author_id']) &
Q(date=b['max_date'])) for b in books])
queryset = Books.objects.filter(filters)
With the combination of .values() and .annotate() we group by the author and annotate the latest date of all books from that author. But the result is a dict and not a queryset.
Then we build a SQL statement like WHERE author_id=X1 AND date=Y1 OR author_id=X2 AND date=Y2.... Now the second query is easy.

How to use Django forms ModelChoiceField with external database values

I am trying to use a ModelChoiceField to get the values populated from an external database.
I have added an additional database in my setting.py and have set up a externaldb.py file in my app as follows:
from django.db import connections
def Location():
rs = []
cursor = connections['mydb'].cursor()
cursor.execute("SELECT city FROM db.data_center WHERE removed is null;")
zones = cursor.fetchall()
for v in zones[::]:
rs.append(v)
The using python manage.py shell I can do this
>>>from platform.externaldb import Location
>>>print Location()
>>>[(u'India-01',), (u'Singapore-01',), (u'Europe-01',)]
So I am getting values but how to I get that to appear in a drop down box.. This is my forms.py
forms.py
from platform.externaldb import Location
zone = forms.ModelChoiceField(Location(), label='Zone')
But this doesn't work for me.. How do I do this so the 3 values appears in the ModelChoiceField drop down list?
Thanks - Oli
You could make use of the ChoiceField form field rather then the ModelChoiceField. The problem with using a ModelChoiceField is that it expects a QuerySet. The ChoiceField allows you to add items via a List instead.
locations = forms.ChoiceField(choices=Locations(), label="Zone")
EDIT
Previously, I had used the ModelChoiceField:
locations = forms.ModelChoiceField(queryset=Location.objects.all(), label="Zone")
which will work as long as Location is a Model (which I wasn't sure of based on your code)