Django, Direction of ManyToMany relations - django-models

I can't find the logic to which of the two related models should have the M2M field? Here is an example from the Django tutorial
class Person(models.Model):
name = models.CharField(max_length=128)
class Group(models.Model):
name = models.CharField(max_length=128)
members = models.ManyToManyField(Person, through='Membership')
class Membership(models.Model):
person = models.ForeignKey(Person)
group = models.ForeignKey(Group)
date_joined = models.DateField()
invite_reason = models.CharField(max_length=64)
what difference would it make if I define the M2M field in Person instead of Group ?
class Person(models.Model):
name = models.CharField(max_length=128)
groups = models.ManyToManyField(Person, through='Membership')
class Group(models.Model):
name = models.CharField(max_length=128)
class Membership(models.Model):
person = models.ForeignKey(Person)
group = models.ForeignKey(Group)
date_joined = models.DateField()
invite_reason = models.CharField(max_length=64)

Technically, you can add a ManyToManyField on either model and get equivalent results, so the decision usually comes down to which model it makes more sense in, and that's usually a very subjective decision.
I usually decide by determining which model the relationship is more integral to: is the set of members more important to a Group than the set of groups is to the Person? If so, the relationship field should go on the Group model.

Related

How can I query across the same M2M field in multiple instances of the same model?

I have the following models:
class VocabEntry(OwnerModelMixin, ModelBase):
prompt_text = models.CharField(max_length=20)
response_text = models.CharField(max_length=20)
image = models.ImageField(blank=True, null=True, upload_to=vocab_upload_path, default=None)
is_public = models.BooleanField(default=False)
last_used = models.DateField()
class Vocab(OwnerModelMixin, ModelBase):
library = GenericRelation(Library)
name = models.CharField(max_length=50)
entries = models.ManyToManyField(VocabEntry, related_name='vocabs')
I am trying to choose the n oldest VocabEntry.
I'm good with just one Vocab model, but I want to combine the results from multiple Vocab models and draw the n oldest VocabEntry from the combined results.
Is there a way to combine the results this way?

How to sort query set (or array) with more than one type of elements (classes) by common attribute using Django Python

I need help with creating a query set (or array) that as more than one type of elements (different classes) by a common attribute.
here are the classes:
class aaa(models.Model):
fd = models.CharField(max_length=100, unique=True)
timestamp = models.DateTimeField(auto_now_add=True, auto_now=False)
updated = models.DateTimeField(auto_now_add=True, auto_now=False)
#more attributes...
def __str__(self):
return self.fd
class bbb(models.Model):
fd = models.CharField(max_length=100)
date = models.DateTimeField(blank=True, null=True)
#more attributes...
timestamp = models.DateTimeField(auto_now_add=True, auto_now=False)
updated = models.DateTimeField(auto_now_add=True, auto_now=False)
class Meta:
ordering = ["-timestamp"]
def __str__(self):
return self.fd
I want to sort all this elements by the attribute "timestamp" (that is the same in the two classes).
So far, this is what i got:
my_objects = (aaa.objects.all(),bbb.objects.all())
That creates the query set of all the elements. I prefer the query set way, if its better to use array or some other structure please let me know.
thanks!
Use the pipe to merge querysets:
combined = qs1 | qs2 | qs3
But this wont work for querysets from distinct models, at least not if they have diffferent fields. You will get an Cannot combine queries on two different base models. error.
A possible solution is to use itertools.chain, like in this blog post, which i quote here for reference.
from itertools import chain
result_lst = list(chain(queryset1, queryset2))
Now, you can sort the resulting list by any common field, e.g. creation date
from operator import attrgetter
result_lst = sorted(
chain(queryset1, queryset2),
key=attrgetter('created_at'))

Django: overriding default field names in ManyToManyField

Good day,
Using a ManyToManyField as in the example below causes django to automatically create a Model called country_region with fields country_id and region_id, which is very cool. But what if the region_country database table (for reasons beyond the scope of this thread) has field names other than country_id and region_id?
Django generates the field names in the many-to-many table based on the model names of the related tables, so the only way I have found to achieve this is to change the two Model names. But that forces me to have model names that I don't want! How can I tell django what field names to use in the automatic many-to-many Model?
I have tried explicitly specifying the many-to-many Model using the through= keyword argument of the ManyToManyField, and this works, but then forms based on the Region Model do not allow saving...
Any advice greatly appreciated.
Thanks,
Randal
class Country(models.Model):
country_id = models.AutoField(primary_key=True)
country_name = models.CharField(max_length=200)
class Meta:
managed = False
db_table = 'country'
def __unicode__(self):
return '%s' % (self.country_name)
class Region(models.Model):
region_id = models.AutoField(primary_key=True)
region_name = models.CharField(max_length=200)
region = models.ManyToManyField(Country, db_table='region_country')
class Meta:
managed = False
db_table = 'region'

Django's ManyToMany Relationship with Additional Fields

I want to store some additional information in that, automatically created, ManyToMany join-table. How would I do that in Django?
In my case I have two tables: "Employees" and "Projects". What I want to store is how much each of the employees receives per hour of work in each of the projects, since those values are not the same. So, how would I do that?
What occurred to me was to, instead of the method "ManyToManyField", create explicitly a third class/table to store those new informations and to set its relationship with "Employees" and "Projects" using the "ForeignKey" method. I'm pretty sure it will work, but is this the best approach?
Here is example of what you want to achieve:
http://docs.djangoproject.com/en/dev/topics/db/models/#extra-fields-on-many-to-many-relationships
In case link ever breaks:
from django.db import models
class Person(models.Model):
name = models.CharField(max_length=128)
def __str__(self): # __unicode__ on Python 2
return self.name
class Group(models.Model):
name = models.CharField(max_length=128)
members = models.ManyToManyField(Person, through='Membership')
def __str__(self): # __unicode__ on Python 2
return self.name
class Membership(models.Model):
person = models.ForeignKey(Person)
group = models.ForeignKey(Group)
date_joined = models.DateField()
invite_reason = models.CharField(max_length=64)

Django Models - FK default to the last entered record (by the logged in user)

I have 2 models in question. JobRecord of which has many Activity(ies), the fk, of course, placed in the Activity model.
To aid data entry, I'd like to default the fk to JobRecord, within a new Activity, to the 'JobRecord fk of the last entered Activity, by the logged in User (bonus points!)'
Here's the model:
class Activity(models.Model):
"""
Record multiple activities against a JobRecord
"""
start = models.TimeField(blank=True, null=True)
end = models.TimeField(blank=True, null=True)
job_record = models.ForeignKey(JobRecord) # editable=False)
task = models.ForeignKey(Task, blank=True, null=True) # show only tasks found in project
flora = models.ManyToManyField(Species, blank=True, null=True, help_text='Optional subject flora.')
memo = models.TextField(blank=True, null=True)
all_operators = models.BooleanField(help_text="Check to include all operators from the parent job record")
operators = models.ManyToManyField(User, null=True, blank=True)
duration = models.DecimalField(blank=True, null=True, max_digits=7, decimal_places=2)
class Meta:
#order_with_respect_to = 'job_record' # not working?
ordering = ['-job_record__date', 'job_record', 'start']
Thanks!
As a very first step, you should store in some model the last activity for each user.
class ActivityLog(models.Model)
user = models.ForeignKey(User)
job_record = models.ForeignKey(JobRecord)
Then in the view, you can retrieve the activity by some simple query:
last_activity_data = ActivityLog.objects.filter(user=request.user)
and use it in initializing the form for the model:
form = ActivityForm(job_record=last_activity_data.job_record)
finally, remember to update the log when saving the Activity.
Note: I have omitted all error checking; for instance, you should take care of the case where there are no last_job_record's, but it should be quite trivial.
Finally, you could also use a ManyToManyField to the Activity, and then using Django's API; it would be perfectly equivalent (e.g., it would create a table very similar to this one behind the scenes), but I think the semantic would be slightly different and for this reason would I prefer the explicit solution.

Resources