Django Model Struct within a Struct? - python

I'm trying to create a structure within a structure under Django's models.py. I know how to do this in C but am new to python.
For example, I have a class:
class OneDay(models.Model)
hour1 = ?
hour2 = ?
...
hour23 = ?
hour24 = ?
Each hour will have a list of identical properties. I want to apply these properties to every single hour within the OneDay class. I don't believe they are ForeignKeys.
class PeriodProperties(models.Model)
isX = boolean
isY = boolean
isZ = boolean
R = number
P = number
So with this I would be able to look up:
hour5.isX == false?
hour10.R > 50%?
Something like that. Can anyone point me in the right direction? Thank you.

You're going to want to check out https://docs.djangoproject.com/en/3.2/topics/db/examples/many_to_one/
Basically, you make:
class OneDay(models.Model)
hour1 = models.ForeignKey("PeriodProperties", on_delete=models.SET_NULL, related_name="hour1s")
...
Then you could do something like:
oneDay.hour1.isX

Related

Django: Can I use a filter to return objects where a condition is valid across an entire related set?

My objects are set up similar to this:
class Event(Model):
pass
class Inventory(Model):
event = OneToOneField(Event)
def has_altered_item_counts(self):
return any(obj.field_one is not None or obj.field_two is not None for obj in self.itemcounts_set.all())
class ItemCounts(Model):
inventory = ForeignKey(Inventory)
field_one = IntegerField(blank=True, null=True)
field_two = IntegerField(blank=True, null=True)
Basically, I'd like to filter uniquely on Event where inventory.has_altered_item_counts would return False
I have
Q(inventory__itemcounts__field_one__isnull=True) & \
Q(inventory__itemcounts__field_two__isnull=True)
but that returns the event every time it meets those conditions. Given that result, I want to exclude that event if the number of times it appears is less than the total number of item counts. Does any of this make sense? Is this possible with filter? I really sort of need it to be, this is part of a programmatically built batch update
I ended up doing it in a "get the PKs I'm trying to exclude first, then exclude those PKs"
Example in case anyone else is interested:
counts = Q(inventory__itemcounts__field_one__isnull=False) | \
Q(inventory__itemcounts__field_two__isnull=False)
bad_pks = set(Event.objects.filter(counts).values_list("pk", flat=True))
Event.objects.exclude(pk__in=bad_pks).update(stuff)
Works like a charm.

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'))).

How do I filter by a date range based on a dictionary value in python, otherwise filter a list by a dictionary?

I have a dictionaries that look like this:
s_dates_dict = {17: datetime.date(2009,21,9,0,24), 19: datetime.datetime(2011,12,1,19,39,16), ....}
e_dates_dict = {17: datetime.date(2010,25,9,10,24), 19: datetime.datetime(2012,1,11,17,39,16), ....}
I want to use these dictionaries to find the next record of type (A or B) after that date for each record. So it seems that I would be best off creating a query similar to below:
next_record_list = Tasks.objects.filter(date__range(s_dates_dict,e_dates_dict), client__in=client_list, task_type__in=[A,B])
Whereby the dictionaries would cause a dynamically changing range to the pk referenced. I haven't found anything suggesting this is possible or efficient, so I am guessing the next best would be creating a list by eliminating the date range filter and then iterating a dictionary of the oldest values using a for statement and cutting off iteration of each record at the date referenced in dates_dict. But I haven't figured out a method to do this either. Could I get some suggestions of how to do this, or a totally different better method? Thanks.
EDIT:
client_list is a list of client objects.
Here is some of the models.py:
class Client(models.Model):
user_name = models.CharField
class Task(models.Model):
client = models.ForeignKey(
'Client',
)
task_type = models.ForeginKey(
'Task_Type',
)
date = models.DateTimeField(
default = datetime.now(),
blank = True,
null = True,
)
class Task_Type(models.Model):
name = models.CharField
I think I understand now. I am assuming client_list is actually a list of client objects. Someone may come up with a more efficient solution, but I would try something like this:
next_record = {}
for client in client_list:
s = s_dates_dict[client.pk]
e = e_dates_dict[client.pk]
next_record[client.pk] = client.tasks_set.filter(date__range(s,e), task_type__in=[A,B])
This gives you a dict (next_record) where each key corresponds to a list of tasks. I am assuming there is a ForeignKey relationship between your Tasks and Client models.
Hopefully this works, if not, can you please post the code for your models?

How to mimic Python set with django ORM?

I am working on a membership application. I would like to make a membership reminder. (member during a period of time which is not member for another period of time).
Currently, I am using set for making this calculation. See the code below.
class Member(models.Model):
...
class Membership(models.Model):
member = models.ForeignKey(Member, verbose_name=_("Member"))
start_date = models.DateField(_("Start date"))
end_date = models.DateField(_("End date"))
x = Member.objects.filter(Q(membership__start_date__lte=dt1) & Q(membership__end_date__gte=dt1))
y = Member.objects.filter(Q(membership__start_date__lte=dt2) & Q(membership__end_date__gte=dt2))
result = set(x) - set(y)
I would like to know of I can do it only by using the django ORM (filter, exclude, annotate, distinct ...)?
Thanks in advance for your help
UPDATE
In fact, my model is a bit more complex. I also have newspaper foreign key.
class Member(models.Model):
...
class Newspaper(models.Model):
...
class Membership(models.Model):
member = models.ForeignKey(Member, verbose_name=_("Member"))
start_date = models.DateField(_("Start date"))
end_date = models.DateField(_("End date"))
newspaper = models.ForeignKey(Newspaper)
I want to have the reminder for a given newspaper. In this case, the working query is
sin = models.Membership.objects.filter(start_date__lte=dt1,
end_date__gte=dt1,
newspaper__id=2)
sout = models.Membership.objects.filter(start_date__lte=dt2,
end_date__gte=dt2,
newspaper__id=2)
result = models.Member.objects.filter(membership__in=sin).exclude(membership__in=sout)
I think that this a more verbose version of the answer given Ghislain Leveque which is also working well for me.
Thanks to S.Lott and KillianDS for very valuable answers and sorry for not so clear question :)
Isn't it simply negating the second expression and putting it in the same filter? So you have something like !(a&b), which equals to (!a)|(!b), in this case:
result = Member.objects.filter(membership__start_date__lte=dt1, membership__end_date__gte=dt1, ~Q(membership__start_date__lte=dt2) | ~Q(membership__end_date__gte=dt2))
note by the way that for simple anding and basic lookups you need no Q objects, like I showed with the first two lookup parameters. Anding happens just by passing multiple arguments, Q objects are needed for negating and OR'ing lookups.
A relational database table is a set -- by definition. Set - is where not exists in SQL, which is exclude in Django's ORM.
It seems (without testing) that you're doing this.
result = Member.objects.filter(
Q(membership__start_date__lte=dt1) & Q(membership__end_date__gte=dt1)
).exclude(
Q(membership__start_date__lte=dt2) & Q(membership__end_date__gte=dt2)
)
You should try :
result = Member.objects.\
filter(
membership__start_date__lte = dt1,
membership__end_date__gte=dt1).\
exclude(
pk__in = \
Member.objects.filter(
membership__start_date__lte = dt2,
membership__end_date__gte = dt2).\
values_list('pk')

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