Exclude field from values() or values_list() - python

Is there an efficient way to exclude fields from the function values() or values_list.
e.g
Videos.objects.filter(id=1).get().values()
I want to exclude from this queryset the field duration.
I know that I can specify fields what I want to have in the result but what if I want everything but only one field not. Like in the cases if I have 20 fields and if I want only one from them not.
Thanks

You must use defer This will not add defined fields to your select query.
Videos.objects.filter(...).defer('duration')

You can get all fields first, then pop out the fields you do not want:
fields = Video._meta.get_all_field_names()
fields.remove('id')
Video.object.filter(...).values(*fields)

Related

Django - Annotate multiple fields from a Subquery

I'm working on a Django project on which i have a queryset of a 'A' objects ( A.objects.all() ), and i need to annotate multiple fields from a 'B' objects' Subquery. The problem is that the annotate method can only deal with one field type per parameter (DecimalField, CharField, etc.), so, in order to annotate multiple fields, i must use something like:
A.objects.all().annotate(b_id =Subquery(B_queryset.values('id')[:1],
b_name =Subquery(B_queryset.values('name')[:1],
b_other_field =Subquery(B_queryset.values('other_field')[:1],
... )
Which is very inefficient, as it creates a new subquery/subselect on the final SQL for each field i want to annotate. I would like to use the same Subselect with multiple fields on it's values() params, and annotate them all on A's queryset. I'd like to use something like this:
b_subquery = Subquery(B_queryset.values('id', 'name', 'other_field', ...)[:1])
A.objects.all().annotate(b=b_subquery)
But when i try to do that (and access the first element A.objects.all().annotate(b=b_subquery)[0]) it raises an exception:
{FieldError}Expression contains mixed types. You must set output_field.
And if i set Subquery(B_quer...[:1], output_field=ForeignKey(B, models.DO_NOTHING)), i get a DB exception:
{ProgrammingError}subquery must return only one column
In a nutshell, the whole problem is that i have multiple Bs that "belongs" to a A, so i need to use Subquery to, for every A in A.objects.all(), pick a specific B and attach it on that A, using OuterRefs and a few filters (i only want a few fields of B), which seens a trivial problem for me.
Thanks for any help in advance!
What I do in such situations is to use prefetch-related
a_qs = A.objects.all().prefetch_related(
models.Prefetch('b_set',
# NOTE: no need to filter with OuterRef (it wont work anyway)
# Django automatically filter and matches B objects to A
queryset=B_queryset,
to_attr='b_records'
)
)
Now a.b_records will be a list containing a's related b objects. Depending on how you filter your B_queryset this list may be limited to only 1 object.

Django postgres order_by distinct on field

We have a limitation for order_by/distinct fields.
From the docs: "fields in order_by() must start with the fields in distinct(), in the same order"
Now here is the use case:
class Course(models.Model):
is_vip = models.BooleanField()
...
class CourseEvent(models.Model):
date = models.DateTimeField()
course = models.ForeignKey(Course)
The goal is to fetch the courses, ordered by nearest date but vip goes first.
The solution could look like this:
CourseEvent.objects.order_by('-course__is_vip', '-date',).distinct('course_id',).values_list('course')
But it causes an error since the limitation.
Yeah I understand why ordering is necessary when using distinct - we get the first row for each value of course_id so if we don't specify an order we would get some arbitrary row.
But what's the purpose of limiting order to the same field that we have distinct on?
If I change order_by to something like ('course_id', '-course__is_vip', 'date',) it would give me one row for course but the order of courses will have nothing in common with the goal.
Is there any way to bypass this limitation besides walking through the entire queryset and filtering it in a loop?
You can use a nested query using id__in. In the inner query you single out the distinct events and in the outer query you custom-order them:
CourseEvent.objects.filter(
id__in=CourseEvent.objects\
.order_by('course_id', '-date').distinct('course_id')
).order_by('-course__is_vip', '-date')
From the docs on distinct(*fields):
When you specify field names, you must provide an order_by() in the QuerySet, and the fields in order_by() must start with the fields in distinct(), in the same order.

Django remove duplicates from queryset

i want to remove duplicates in relative fields, my queryset example:
example = models.Object.objects.values('name', 'photo__name', 'url', 'photo__url').distinct()
if name == photo__name and url == photo_url i need to delete one of them, how can i do this with Django ORM or i need to iterate through queryset?
If you are using PostgreSQL, check out the Django docs on distinct():
On PostgreSQL only, you can pass positional arguments (*fields) in order to specify the names of fields to which the DISTINCT should apply...
When you specify field names, you must provide an order_by() in the QuerySet, and the fields in order_by() must start with the fields in distinct(), in the same order.
Thus, in your example, you can remove duplicates on certain fields by using:
.order_by('photo__name', 'photo__url').distinct('photo__name', 'photo__url')
To reference fields of model in filtering you can use Django ORM F function: https://docs.djangoproject.com/en/dev/topics/db/queries/#filters-can-reference-fields-on-the-model
But i guess you cannot delete one of them :) You got to decide which one you want to delete
UPDATE
Look when you filter like Object.objects.filter(photo__name='something') you filter Object table by related photo name. So you dealing with join over two tables. If you want to exclude objects with name = related photo name you should do something like this
from django.db.models import F
Object.objects.exclude(name=F('photo__name'))
Is that useful?

Running into issues with Django's ModelChoiceField "queryset" attribute

In django by default the form for a model foreign key is a ModelChoiceField (where you can select out of a list of all possible models). And it's possible to change these with the query set attribute, like
// in forms.py
self.fields['possible_cars'].queryset = somequeryset
But I'm in a situation where I have a list of stuff, not a queryset, and since there is no way to convert a list into a queryset, i'm not sure how to make the options for my ModelChoiceField similar to the list of models I want. (Since they take a queryset by default, i'm assuming they get a list from that query anyways, so this kinda thing should be possible).
I tried self.fields['possible_cars']._choices = mylist , but it won't work.
Any ideas guys?
Assuming your field take a Car queryset, you can construct one like the following:
mylist = ['BMW', 'Lamborghini', 'Porsche']
cars = Car.objects.filter(name__in=mylist)
self.fields['possible_cars'].queryset = cars

How to obtain a QuerySet of all rows, with specific fields for each one of them?

I have a model Employees and I would like to have a QuerySet of all rows, but with some specific fields from each row, and not all fields.
I know how to query all rows from the table/model:
Employees.objects.all()
I would like to know how to select fields for each of the Queryset element. How can I do that?
Employees.objects.values_list('eng_name', flat=True)
That creates a flat list of all eng_names. If you want more than one field per row, you can't do a flat list: this will create a list of tuples:
Employees.objects.values_list('eng_name', 'rank')
In addition to values_list as Daniel mentions you can also use only (or defer for the opposite effect) to get a queryset of objects only having their id and specified fields:
Employees.objects.only('eng_name')
This will run a single query:
SELECT id, eng_name FROM employees
We can select required fields over values.
Employee.objects.all().values('eng_name','rank')
Daniel answer is right on the spot. If you want to query more than one field do this:
Employee.objects.values_list('eng_name','rank')
This will return list of tuples. You cannot use named=Ture when querying more than one field.
Moreover if you know that only one field exists with that info and you know the pk id then do this:
Employee.objects.values_list('eng_name','rank').get(pk=1)
Oskar Persson's answer is the best way to handle it because makes it easier to pass the data to the context and treat it normally from the template as we get the object instances (easily iterable to get props) instead of a plain value list.
After that you can just easily get the wanted prop:
for employee in employees:
print(employee.eng_name)
Or in the template:
{% for employee in employees %}
<p>{{ employee.eng_name }}</p>
{% endfor %}
You can use values_list alongside filter like so;
active_emps_first_name = Employees.objects.filter(active=True).values_list('first_name',flat=True)
More details here
Employees.objects.filter().only('eng_name')
This will give a QuerySet of all rows, but with only the specified fields. It does NOT give a list like the other answers above. It gives your the OBJECT INSTANCES. Watch out; it is objects.filter().only() NOT just objects.only()
It is similar to SQL Query:
SELECT eng_name FROM Employees;
queryset = ModelName.objects.filter().only('field1', 'field2')

Categories

Resources