I have been struggling all morning trying to figure out how to compare two different querysets. I have two manytomanyfields in my model, and I'm trying to figure out if they are identical.
I research this by viewing this particular issue: How do I test Django QuerySets are equal?
I am using class based views...and I have a model with two manytomanyfields...
My model...
class Author(models.Model):
title = models.ManyToManyField(User,blank=True,related_name='title')
title1 = models.ManyToManyField(User,blank=True,related_name='title1)
My View...
class AuthorDetailView(DetailView):
model = Author
def get_context_data(self, **kwargs):
context = super(AuthorDetailView, self).get_context_data(**kwargs)
title = list(Author.objects.filter(title))
title1 = list(Author.objects.filter(title1))
test_instance = Author.objects.all()
proxy4 = self.assertQuerySetEqual(Author.objects.all(), map(repr, [test_instance]))
I am trying to compare fields title and title1. However when try to run the code above I continually get a 'View' object has no attribute 'assertQuerysetEqual'. I can't even get this function to work. I running Django 1.11 and Postgresql. Perhaps this function doesn't work with Postgresql? Any help to get me on the right track is appreciated. Been playing with this and researching all morning with no luck. Thanks in advance.
Update...I have also been playing with various versions of trying to compare title_set.all and title1_set.all in the views....this is working intermittently...but the two are always returning that they are equal.
self.assertQuerySetEqual is about unit-testing.
All you need is compare two lists in your view. It can be done with sets.
set(title) == set(title1)
Your AuthorDetailView need to inherits from django.test.TestCase in order to get access to self.assertQuerySetEqual which is a testing feature.
So basically you need to do like this example:
from django.test import TestCase
...
class AuthorDetailView(DetailView, TestCase):
...
Otherwise for comparing two lists you can use set() like what #Daniil Mashkin said in his answer.
So, more in depth, you can do something like this:
class AuthorDetailView(DetailView):
model = Author
def get_context_data(self, **kwargs):
context = super(AuthorDetailView, self).get_context_data(**kwargs)
title = list(Author.objects.all().values_list('title'))
title1 = list(Author.objects.all().values_list('title1'))
# test if the two lists are equal
equal_ = set(title) == set(title1)
# add the result to the context variable
context.update({'titles_are_equal': equal_})
# return it in order to get the variable `title_are_equal`
# into your template
return context
For more informations visit the django official documentaiton
Related
I'am just looking for answer for my (seems to be stupid) question. I've already watched few stackoverflow posts but any of them was helpful :(
My question is how to compare two instance of different models with different?
Here is my case:
I've got two models: Product and Connector. First include id(pk), name, ect. Another include id(pk), productId(fk), userId(fk), ect.
My goal is to prepare view that list only product which are in Connector db-table as product(fk).
def list(self, request, *args, **kwargs):
# return only product user's watching
userId = self.request.user.id
connectorData = ConnectorModel.objects.filter(userId=userId)
allProducts = self.get_queryset()
productListToDisplay = []
for product in allProducts:
for connector in connectorData:
if product.id == connector.productId:
# HERE IS A PROBLEM
productListToDisplay.append(product)
serializer = ProductSerializer(productListToDisplay, many=True)
return Response(serializer.data)
Problem is that Django consider "product.id" and "connector.productId" as totally different types. Firs is "core.models.ProductModel" and second is
"core.models.ConnectorModel". I was trying to parse it using int() but it generate errors.
How I can compare this two values to add object to productListToDisplay array?
(I saw django doc - Comparing objects but there is no helpful information for this case)
This should work
connectorData = ConnectorModel.objects.filter(userId=userId, productId__in=all_products)
for connector in connectorData:
productListToDisplay.append(connector.product)
You could do this:
productListToDisplay = ConnectorModel.objects.filter(userId=userId, product__isnull=False).prefetch_related('product').values_list('product', flat=True)
I have a model class called Poll which has title, description, and tags fields. I also have a PollEntry class (related_name=entries) which also has title, description, and tags. I am trying to implement text search (using contrib.postgres.search) module. There are apparently issues with returning a search ordered by rank returning duplicate objects and the Django documentation says basically "You have to be careful", but does not give any examples of how to deal with this, and I have had basically no luck finding examples online (on SO or elsewhere).
The following code snippet appears to solve this problem, but I don't know if it is the most efficient way to do this. Any suggestions or references would be much appreciated! Also, note I am using DRF here.
#list_route(methods=['get'])
def search(self, request):
search_query_terms = request.query_params.get('searchQuery').split(' ')
search_vector = SearchVector('entries__tags__name')+\
SearchVector('title')+\
SearchVector('description')+\
SearchVector('tags__name')+\
SearchVector('entries__title')+\
SearchVector('entries__description')
search_query = SearchQuery(search_query_terms[0])
for term in search_query_terms[1:]:
search_query = SearchQuery(term) | search_query
ids = Poll.objects\
.annotate(rank=SearchRank(search_vector, search_query))\
.order_by('-rank')\
.filter(rank__gt=0)\
.values_list('id')
polls = Poll.objects.filter(id__in=ids)
serializer = self.get_serializer(polls, many=True)
return Response(
data=serializer.data
)
While creating a front end for a Django module I faced the following problem inside Django core:
In order to display a link to the next/previous object from a model query, we can use the extra-instance-methods of a model instance: get_next_by_FIELD() or get_previous_by_FIELD(). Where FIELD is a model field of type DateField or DateTimeField.
Lets explain it with an example
from django.db import models
class Shoe(models.Model):
created = models.DateTimeField(auto_now_add=True, null=False)
size = models.IntegerField()
A view to display a list of shoes, excluding those where size equals 4:
def list_shoes(request):
shoes = Shoe.objects.exclude(size=4)
return render_to_response(request, {
'shoes': shoes
})
And let the following be a view to display one shoe and the corresponding
link to the previous and next shoe.
def show_shoe(request, shoe_id):
shoe = Shoe.objects.get(pk=shoe_id)
prev_shoe = shoe.get_previous_by_created()
next_shoe = shoe.get_next_by_created()
return render_to_response('show_shoe.html', {
'shoe': shoe,
'prev_shoe': prev_shoe,
'next_shoe': next_shoe
})
Now I have the situation that the show_shoe view displays the link to the previous/next regardless of the shoes size. But I actually wanted just shoes whose size is not 4.
Therefore I tried to use the **kwargs argument of the get_(previous|next)_by_created() methods to filter out the unwanted shoes, as stated by the documentation:
Both of these methods will perform their queries using the default manager for the model. If you need to emulate filtering used by a custom manager, or want to perform one-off custom filtering, both methods also accept
optional keyword arguments, which should be in the format described in Field lookups.
Edit: Keep an eye on the word "should", because then also (size_ne=4) should work, but it doesn't.
The actual problem
Filtering using the lookup size__ne ...
def show_shoe(request, shoe_id):
...
prev_shoe = shoe.get_previous_by_created(size__ne=4)
next_shoe = shoe.get_next_by_created(size__ne=4)
...
... didn't work, it throws FieldError: Cannot resolve keyword 'size_ne' into field.
Then I tried to use a negated complex lookup using Q objects:
from django.db.models import Q
def show_shoe(request, shoe_id):
...
prev_shoe = shoe.get_previous_by_created(~Q(size=4))
next_shoe = shoe.get_next_by_created(~Q(size=4))
...
... didn't work either, throws TypeError: _get_next_or_previous_by_FIELD() got multiple values for argument 'field'
Because the get_(previous|next)_by_created methods only accept **kwargs.
The actual solution
Since these instance methods use the _get_next_or_previous_by_FIELD(self, field, is_next, **kwargs) I changed it to accept positional arguments using *args and passed them to the filter, like the **kwargs.
def my_get_next_or_previous_by_FIELD(self, field, is_next, *args, **kwargs):
"""
Workaround to call get_next_or_previous_by_FIELD by using complext lookup queries using
Djangos Q Class. The only difference between this version and original version is that
positional arguments are also passed to the filter function.
"""
if not self.pk:
raise ValueError("get_next/get_previous cannot be used on unsaved objects.")
op = 'gt' if is_next else 'lt'
order = '' if is_next else '-'
param = force_text(getattr(self, field.attname))
q = Q(**{'%s__%s' % (field.name, op): param})
q = q | Q(**{field.name: param, 'pk__%s' % op: self.pk})
qs = self.__class__._default_manager.using(self._state.db).filter(*args, **kwargs).filter(q).order_by('%s%s' % (order, field.name), '%spk' % order)
try:
return qs[0]
except IndexError:
raise self.DoesNotExist("%s matching query does not exist." % self.__class__._meta.object_name)
And calling it like:
...
prev_shoe = shoe.my_get_next_or_previous_by_FIELD(Shoe._meta.get_field('created'), False, ~Q(state=4))
next_shoe = shoe.my_get_next_or_previous_by_FIELD(Shoe._meta.get_field('created'), True, ~Q(state=4))
...
finally did it.
Now the question to you
Is there an easier way to handle this? Should shoe.get_previous_by_created(size__ne=4) work as expected or should I report this issue to the Django guys, in the hope they'll accept my _get_next_or_previous_by_FIELD() fix?
Environment: Django 1.7, haven't tested it on 1.9 yet, but the code for _get_next_or_previous_by_FIELD() stayed the same.
Edit: It is true that complex lookups using Q object is not part of "field lookups", it's more part of the filter() and exclude() functions instead. And I am probably wrong when I suppose that get_next_by_FIELD should accept Q objects too. But since the changes involved are minimal and the advantage to use Q object is high, I think these changes should get upstream.
tags: django, complex-lookup, query, get_next_by_FIELD, get_previous_by_FIELD
(listing tags here, because I don't have enough reputations.)
You can create custom lookup ne and use it:
.get_next_by_created(size__ne=4)
I suspect the method you've tried first only takes lookup arg for the field you're basing the get_next on. Meaning you won't be able to access the size field from the get_next_by_created() method, for example.
Edit : your method is by far more efficient, but to answer your question on the Django issue, I think everything is working the way it is supposed to. You could offer an additional method such as yours but the existing get_next_by_FIELD is working as described in the docs.
You've managed to work around this with a working method, which is OK I guess, but if you wanted to reduce the overhead, you could try a simple loop :
def get_next_by_field_filtered(obj, field=None, **kwargs):
next_obj = getattr(obj, 'get_next_by_{}'.format(field))()
for key in kwargs:
if not getattr(next_obj, str(key)) == kwargs[str(key)]:
return get_next_by_field_filtered(next_obj, field=field, **kwargs)
return next_obj
This isn't very efficient but it's one way to do what you want.
Hope this helps !
Regards,
Firstly, I did my homework and looked around before posting! My question seems like a very basic thing that must’ve been covered before.
I'm now looking at Django-filter as a potential solution, but would like some advice on if this is the right way to go and if there any other solutions.
I have a Django app wit 10 models, each model has a few fields. Most fields are ChoiceField that users populate using forms with the default select widget. There is a separate form for each model.
I want to create a separate form for each model (in separate views) that users will use to search the database. The search form will contain only drop-down boxes (the select widgets) with the same choices as the forms used to populate the database with the addition of the “any” option.
I know how to use .object.filter(), however the “any” option would correspond to not include specific fields in the filter and I'm not sure how to add model fields to the filter based on users’ selection
I briefly looked at Haystack as an option but it seems to be made for full text search rather than “model filed search” I'm after.
Sample model (simplified):
class Property():
TYPE_CHOICES = (‘apartment’, ‘house’, ‘flat’)
type = charfield(choices=TYPE_CHOICES)
LOC_CHOICES = (‘Brussels’, ‘London’, ‘Dublin’, ‘Paris’)
location = charfield(choices=LOC_CHOICES)
price = PostivieInteger()
Users can select only “type”, only “location” or both (not making selection is equal to ANY) in which case I end up with 3 different filters:
Property.objects.filter(type=’apartment’)
Property.objects.filter(location=’Dublin’)
Property.objects.filter(type=’apartment’, location=’Dublin’)
The main question: django-filter the best option?
Question 1: what’s the best option of accomplishing this overall?
Question 2: how do I add model fields to the filter based on user’s form selection?
Question 3: how do I do the filter based on user selection? (I know how to use .filter(price_lt=).exclude(price_gt=) but again how do I do it dynamically based on selection as “ANY” would mean this is not included in the query)
I had a similar case like yours (real estate project), I ended up with the following approach, you can refine this to your needs...I removed select_related and prefetch_related models for easier reading
properties/forms.py:
class SearchPropertyForm(forms.Form):
property_type = forms.ModelChoiceField(label=_("Property Type"), queryset=HouseType.objects.all(),widget=forms.Select(attrs={'class':'form-control input-sm'}))
location = forms.ModelChoiceField(label=_('Location'), queryset=HouseLocation.objects.all(), widget=forms.Select(attrs={'class':'form-control input-sm'}))
Then in the properties/views.py
# Create a Mixin to inject the search form in our context
class SeachPropertyMixin(object):
def get_context_data(self, **kwargs):
context = super(SeachPropertyMixin, self).get_context_data(**kwargs)
context['search_property_form'] = SearchPropertyForm()
return context
In your actual view (I apply the search form as a sidebar element in my detailview only:
# Use Class Based views, saves you a great deal of repeating code...
class PropertyView(SeachPropertyMixin,DetailView):
template_name = 'properties/view.html'
context_object_name = 'house'
...
queryset = HouseModel.objects.select_related(...).prefetch_related(...).filter(flag_active=True, flag_status='a')
Finally your search result view (this is performed as GET request, since we are not altering any data in our DB, we stick to the GET method):
# Search results should return a ListView, here is how we implement it:
class PropertySearchResultView(ListView):
template_name = "properties/propertysearchresults.html"
context_object_name = 'houses'
paginate_by = 6
queryset = HouseModel.objects.select_related(...).prefetch_related(...).order_by('-sale_price').filter(flag_active=True, flag_status='a')
def get_queryset(self):
qs = super(PropertySearchResultView,self).get_queryset()
property_type = self.request.GET.get('property_type')
location = self.request.GET.get('location')
'''
Start Chaining the filters based on the input, this way if the user has not
selected a filter it wont be used.
'''
if property_type != '' and property_type is not None:
qs = qs.filter(housetype=property_type)
if location != '' and location is not None:
qs = qs.filter(location=location)
return qs
def get_context_data(self, **kwargs):
context = super(PropertySearchResultView, self).get_context_data()
'''
Add the current request to the context
'''
context['current_request'] = self.request.META['QUERY_STRING']
return context
Your solution works. I've modified it and I'm not using ModelChoiceField but the standard form.ChoiceField. The reason for that is that I wanted to add option "Any". My "if" statements look like:
if locality != 'Any Locality':
qs = qs.filter(locality=locality)
if property_type != 'Any Type':
qs = qs.filter(property_type=property_type)
if int(price_min) != 0:
qs = qs.filter(price__gte=price_min)
if int(price_max) != 0:
qs = qs.filter(price__lte=price_max)
if bedrooms != 'Any Number':
qs = qs.filter(bedrooms=bedrooms)
And so on....
This does the job, however it seems like an ugly and hacky solution to a simple problem. I would think is a common use case. I feel there should be a cleaner solution...
I've tried the django-filter. It is close to doing what I want but I couldn't add the "Any" choice and it filters inline rather than returning. It should do with some modifications.
Cheers
Say I have django model that looks something like this:
class Order(models.Model):
number = models...
date = models...
class OrderLine(models.Model):
# One or more lines per order
order = models.ForeginKey(Order)
common_line = models.OneToOneField(CommonLine)
class CommonLine(models.Model):
# common elements of what might be on a line item...
taxes = model...
amount = model...
I want to create a form that uses an inlineformset to edit one or more Lines (both OrderLine and CommonLine) per order.
I can create a formset that works with Order and OrderLine - but how do I get the inline formset to give me all the detailed items from the CommonLine class when displaying the formset. It seems the documentation on inline formsets requires that the inline form - the multiple lines on an order can only map to a single class...
Am I not seeing something in the documentation? I'm sure I can probably override something, I'm just not sure where.
Thanks for any help...
I solved problem with http://yergler.net/blog/2009/09/27/nested-formsets-with-django/. Pleas use the following correction in forms.py file:
instance=None
pk_value = hash(form.prefix)
+ correct_data = None
+ if (self.data):
+ correct_data = self.data;
# store the formset in the .nested property
form.nested = [
- TenantFormset(data=self.data,
+ TenantFormset(data=correct_data,
instance = instance,
Just working on Django 1.4.1 very well.
Some minor changes were needed to make Nathan's code at http://yergler.net/blog/2009/09/27/nested-formsets-with-django/ work in Django 1.3. The line below causes a ManagementForm Error.
TenantFormset = inlineformset_factory(models.Building, models.Tenant, extra=1)
Usings the modelformset_factory and manually defining the queryset seems to work, but I have not implemented the ability to add extras.
TenantFormset = modelformset_factory(models.Tenant, extra=0)
form.nested = [
TenantFormset(
queryset = Tenant.objects.filter(building = pk_value),
prefix = 'value_%s' % pk_value
)
]
I also had to manually pass data to the sub-sub-forms in the is_valid method:
def is_valid(self):
result = super(BaseProtocolEventFormSet, self).is_valid()
for form in self.forms:
if hasattr(form, 'nested'):
for n in form.nested:
n.data = form.data
if form.is_bound:
n.is_bound = True
for nform in n:
nform.data = form.data
if form.is_bound:
nform.is_bound = True
# make sure each nested formset is valid as well
result = result and n.is_valid()
return result
EDIT:
New instances can be created using jQuery. See this question:
This sounds very similar to the approach talked about at help http://yergler.net/blog/2009/09/27/nested-formsets-with-django/ where Nathan writes about how he catered for "a multi-level data model; an example of this kind of model would be modeling City Blocks, where each Block has one or more Buildings, and each Building has one or more Tenants."
Some more explanations can aslo be found here Django Forms Newbie Question