TestCase self.assertEqual does not match a similar string - python

I'am trying to create a model unittest for a ManyToMany relationship.
The aim is to check, if there is the right category saved in the table Ingredient.
class IngredientModelTest(TestCase):
def test_db_saves_ingredient_with_category(self):
category_one = IngredientsCategory.objects.create(name='Food')
first_Ingredient = Ingredient.objects.create(name='Apple')
first_Ingredient.categories.add(category_one)
category_two = IngredientsCategory.objects.create(name='Medicine')
second_Ingredient = Ingredient.objects.create(name='Antibiotics')
second_Ingredient.categories.add(category_two)
first_ = Ingredient.objects.first()
self.assertEqual('Apple', first_.name)
self.assertEqual(first_.categories.all(), [category_one])
self.assertEqual(first_, first_Ingredient)
for self.asserEqual(first_.categories.all(), [category_one]) in the second last row I get this weird assert:
AssertionError: [<IngredientsCategory: Food>] != [<IngredientsCategory: Food>]
I tried many other different ways, but none of it worked. Does any one suppose how I can get the information of first_.categories.all() to compare it with something else?

That'll be because they're not equal - one is a QuerySet, the other is a list - they just happen to have the same str representations.
You could either cast the QuerySet to a list with list(first_.categories.all()), or a possible solution for this situation may be:
self.assertEqual(first_.categories.get(), category_one)

Related

Django __in but return the first matching element

I have this model
from django.db import models
class TranslatedString(models.Model):
lang = models.CharField()
key = models.CharField()
value = models.CharField()
I have these instances of this model:
a = TranslatedString(lang="en_US", key="my-string", value="hello world")
b = TranslatedString(lang="en_AU", key="my-string", value="g'day world")
c = TranslatedString(lang="ja_JP", key="my-string", value="こんにちは世界")
And I have this list of languages a user wants
preferred_langs = ["en_CA", "en_US", "en_AU", "fr_CA"]
which is ordered by preference. I would like to return the value that matches the first item in that list. Even though both a and b would match a query like
TranslatedString.objects.filter(key="my-string", lang__in=preferred_langs).first()
I want it to be ordered by the list, so that I always get a.
I can make a query for each element in preferred_langs and return as soon as I find a matching value, but is there a better option? I'd like to do it in one query.
You can use a generator expression over preferred_langs to produce a mapping of preferred languages to their respective indices in the list as When objects for a Case object to be annotated as a field so that you can order the filtered result by it:
from django.db.models import Case, Value, When
TranslatedString.objects.filter(key="my-string", lang__in=preferred_langs).annotate(
preference=Case(*(When(lang=lang, then=Value(i)) for i, lang in preferred_langs))
).order_by('preference').first()
If you don't mind retrieving all preferred translations from the database, this may be accomplished tersely by sorting the models in Python:
preferred_langs = ["en_CA", "en_US", "en_AU", "fr_CA"]
strings = list(TranslatedString.objects.filter(key="my-string", lang__in=preferred_langs))
strings.sort(key=lambda s: preferred_langs.index(s))
first_choice = strings[0]
print(first_choice.lang) # outputs "en_US"
This will perform a single (but potentially large) query. However, if the sequence of preferred languages is fairly short, the sorting should occur in negligible time.

queryset filter according to only first line - Django

Tying to filter data using queryset.filter() in Django. but it returns not what I expecting. can someone correct me.
single data cell looks like below.(each line separated by \n)
こちらは1行です。(0.57)\n
こちらは2行です。(0.67)\n
こちらは3行です。(0.77)\n
こちらは4行です。(0.87)\n
こちらは5行です。(0.697)
code like below
queryset = queryset.filter(predicted_result__regex = r"\A.*", predicted_result__contains='(0.5') |\
queryset.filter(predicted_result__regex = r"\A.*", predicted_result__contains='(0.6') |\
queryset.filter(predicted_result__regex = r"\A.*", predicted_result__contains='(0.7')
output:
this will be considering all 5 lines not only the first line.
target:
only get values contains in first line between score (inside brackets)0.5 to 0.8. all the other lines should omit.
expected result:
こちらは1行です。(0.57)\n
The problem with your current query is that your 'single data cell' is currently a string. Using a regex to calculate what may or may not be expected will still return the entire string on 'True'. You should separate the 'text' from the 'float' values in order to query more efficiently.
class Model(model):
descriptor = models.CharField()
value = models.FloatField()
Now you can actually query the 'value' on it's own.
If you are unable to change the model for whatever reason, you need to do multiple queries and use the union/intersection/difference methods.
fives_queryset = queryset.filter(predicted_result__contains'(0.5')
sixes_queryset = queryset.filter(predicted_result__contains'(0.6')
sevens_queryset = queryset.filter(predicted_result__contains'(0.7')
result_queryset = union(fives_queryset, sixes_queryset, sevens_queryset)

Django Q set too many values to unpack

I am new to django and trying to filter multiple fields that contain text.
columns = ['ticketId', 'checkSum']
q_objects = [Q(fieldname +'__contains', myString) for fieldname in columns]
objects = objects.filter(reduce(operator.or_, q_objects))
I get
Exception Type: ValueError
Exception Value: too many values to unpack on the "filter" last line. Any ideas
Try this:
Q(**{fieldname + '__contains': myString})
This is equivalent to providing a keyword argument, as you normally would when instantiating a Q object. For example:
Q(fieldname__contains=myString, another_fieldname__contains=myOtherstring)
The Q object essentially needs pairs of values to work. Looking at the code it seems you can also use tuples of length two, like this (I haven't tested, though):
Q(("fieldname__contains", myString), ("another_fieldname__contains", myOtherString))
What is the model you are querying? It looks like you left that out.
The last line,
objects = objects.filter(reduce(operator.or_, q_objects))
Should be something like
objects = MyModel.objects.filter(...)

how to add a condition of comparing two fields of the model itself in Django model query

As mentioned by the title, in Django:
Say I have a model name QuestionRecord, with two fields: full_score and actual_score.
I want to realize the SQL:
select * from QuestionRecord as QR where QR.full_score!=QR.actual_score.
Maybe using raw sql is OK, but I want to implement it like this:
class QuestionRecord_QuerySet(models.query.QuerySet):
def incorrect(self):# Find out those whose full_score and actual_score are not equal
return self.filter(...) # **What should I write here??**
class QuestionRecord_Manager(models.Manager):
def get_query_set(self):
return QuestionRecord_QuerySet(self.model)
class QuestionRecord(models.Model):
objects = QuestionRecord_Manager()
Is there any way to do it?
Sure, that's what the "F" object is for!
from django.db.models import F
# snip
return self.exclude(full_score = F('actual_score'))
Use QuerySet.exclude here, as you want to not get the results that match.
If you really want to use QuerySet.filter, you can use a negated "Q" object: self.filter(~Q(full_score = F('actual_score'))).

Filter by property

Is it possible to filter a Django queryset by model property?
i have a method in my model:
#property
def myproperty(self):
[..]
and now i want to filter by this property like:
MyModel.objects.filter(myproperty=[..])
is this somehow possible?
Nope. Django filters operate at the database level, generating SQL. To filter based on Python properties, you have to load the object into Python to evaluate the property--and at that point, you've already done all the work to load it.
I might be misunderstanding your original question, but there is a filter builtin in python.
filtered = filter(myproperty, MyModel.objects)
But it's better to use a list comprehension:
filtered = [x for x in MyModel.objects if x.myproperty()]
or even better, a generator expression:
filtered = (x for x in MyModel.objects if x.myproperty())
Riffing off #TheGrimmScientist's suggested workaround, you can make these "sql properties" by defining them on the Manager or the QuerySet, and reuse/chain/compose them:
With a Manager:
class CompanyManager(models.Manager):
def with_chairs_needed(self):
return self.annotate(chairs_needed=F('num_employees') - F('num_chairs'))
class Company(models.Model):
# ...
objects = CompanyManager()
Company.objects.with_chairs_needed().filter(chairs_needed__lt=4)
With a QuerySet:
class CompanyQuerySet(models.QuerySet):
def many_employees(self, n=50):
return self.filter(num_employees__gte=n)
def needs_fewer_chairs_than(self, n=5):
return self.with_chairs_needed().filter(chairs_needed__lt=n)
def with_chairs_needed(self):
return self.annotate(chairs_needed=F('num_employees') - F('num_chairs'))
class Company(models.Model):
# ...
objects = CompanyQuerySet.as_manager()
Company.objects.needs_fewer_chairs_than(4).many_employees()
See https://docs.djangoproject.com/en/1.9/topics/db/managers/ for more.
Note that I am going off the documentation and have not tested the above.
Looks like using F() with annotations will be my solution to this.
It's not going to filter by #property, since F talks to the databse before objects are brought into python. But still putting it here as an answer since my reason for wanting filter by property was really wanting to filter objects by the result of simple arithmetic on two different fields.
so, something along the lines of:
companies = Company.objects\
.annotate(chairs_needed=F('num_employees') - F('num_chairs'))\
.filter(chairs_needed__lt=4)
rather than defining the property to be:
#property
def chairs_needed(self):
return self.num_employees - self.num_chairs
then doing a list comprehension across all objects.
I had the same problem, and I developed this simple solution:
objects = [
my_object
for my_object in MyModel.objects.all()
if my_object.myProperty == [...]
]
This is not a performatic solution, it shouldn't be done in tables that contains a large amount of data. This is great for a simple solution or for a personal small project.
PLEASE someone correct me, but I guess I have found a solution, at least for my own case.
I want to work on all those elements whose properties are exactly equal to ... whatever.
But I have several models, and this routine should work for all models. And it does:
def selectByProperties(modelType, specify):
clause = "SELECT * from %s" % modelType._meta.db_table
if len(specify) > 0:
clause += " WHERE "
for field, eqvalue in specify.items():
clause += "%s = '%s' AND " % (field, eqvalue)
clause = clause [:-5] # remove last AND
print clause
return modelType.objects.raw(clause)
With this universal subroutine, I can select all those elements which exactly equal my dictionary of 'specify' (propertyname,propertyvalue) combinations.
The first parameter takes a (models.Model),
the second a dictionary like:
{"property1" : "77" , "property2" : "12"}
And it creates an SQL statement like
SELECT * from appname_modelname WHERE property1 = '77' AND property2 = '12'
and returns a QuerySet on those elements.
This is a test function:
from myApp.models import myModel
def testSelectByProperties ():
specify = {"property1" : "77" , "property2" : "12"}
subset = selectByProperties(myModel, specify)
nameField = "property0"
## checking if that is what I expected:
for i in subset:
print i.__dict__[nameField],
for j in specify.keys():
print i.__dict__[j],
print
And? What do you think?
i know it is an old question, but for the sake of those jumping here i think it is useful to read the question below and the relative answer:
How to customize admin filter in Django 1.4
It may also be possible to use queryset annotations that duplicate the property get/set-logic, as suggested e.g. by #rattray and #thegrimmscientist, in conjunction with the property. This could yield something that works both on the Python level and on the database level.
Not sure about the drawbacks, however: see this SO question for an example.

Categories