I have two models Category and Entry. There is another model ExtEntry that inherits from Entry
class Category(models.Model):
title = models.CharField('title', max_length=255)
description = models.TextField('description', blank=True)
...
class Entry(models.Model):
title = models.CharField('title', max_length=255)
categories = models.ManyToManyField(Category)
...
class ExtEntry(Entry):
groups= models.CharField('title', max_length=255)
value= models.CharField('title', max_length=255)
...
I am able to use the Category.entry_set but I want to be able to do Category.blogentry_set but it is not available. If this is not available,then I need another method to get all ExtEntryrelated to one particular Category
EDIT
My end goal is to have a QuerySet of ExtEntry objects
Thanks
I need another method to get all ExtEntryrelated to one particular Category
Easy:
ExtEntry.objects.filter(categories=my_category)
Do you know if there is a way to use the _set feature of an inherited
I don't know if there is a direct they for that. It is not mentioned in documentation.
But it is possible to get similar results with the select_related.
for e in category.entry_set.select_related('extentry'):
e.extentry # already loaded because of `select_related`,
# however might be None if there is no Extentry for current e
It is possible to select only entries which has ExtEntry:
for e in category.entry_set.select_related('extentry').exlude(extentry=None):
e.extentry # now this definitely is something, not None
Bad thing about the exclude is that it generates terrybly inefficient query:
SELECT entry.*, extentry.* FROM entry
LEFT OUTER JOIN `extentry` ON (entry.id = extentry.entry_ptr_id)
WHERE NOT (entry.id IN (SELECT U0.id FROM entry U0 LEFT OUTER JOIN
extentry U1 ON (U0.id = U1.entry_ptr_id)
WHERE U1.entry_ptr_id IS NULL))
So my resume would be: use ExtEntry.objects.filter() to get your results. The backwards relations (object.something_set) is just a convenience and does not work in every situation.
See the documentation here for an explanation of how this works.
Basically, since you can get the parent model item, you should be able to get its child because an implicit one-to-one linkage is created.
The inheritance relationship introduces links between the child model and each of its parents (via an automatically-created OneToOneField).
So, you should be able to do:
categories = Category.objects.all()
for c in categories:
entries = c.entry_set.all()
for e in entries:
extentry = e.extentry
print extentry.value
It isn't documented that I can see, but I believe that generally, your one-to-one field name will be a lower class version of the inheriting model name.
The problem your running into is because Entry and ExtEntry are in separate tables. This may be the best solution for you, but you should be aware of that when you choose to use multi-table inheritance.
Something like category.entry_set.exclude(extentry=None) should work for you.
Related
Suppose I have the following models:
class User(models.Model):
# ... some fields
class Tag(models.Model):
# ... some fields
class UserTag(models.Model):
user = models.ForeignKey(User, related_name='tags')
tag = models.ForeignKey(Tag, related_name='users')
date_removed = models.DateTimeField(null=True, blank=True)
Now I lets say I want to get all the users that have a given tag that has not yet been removed (ie date_removed=None). If I didn't have to worry about the date_removed constraint, I could do:
User.objects.filter(tags__tag=given_tag)
But I want to get all users who have that given tag and have the tag without a date_removed on it. Is there an easy way in Django to get that in a single queryset? And assume I have millions of Users, so getting any sort of list of User IDs and keeping it in memory is not practical.
Your filter() call can include multiple constraints:
User.objects.filter(tags__tag=given_tag, tags__date_removed=None)
When they match, they will both match to the same Tag, not two possibly different ones.
See the documentation on spanning multi-valued relationships;
in particular, the difference between filter(a, b) and filter(a).filter(b).
I am creating a web application to manage robotics teams for our area. In the application I have a django model that looks like this:
class TeamFormNote(models.Model):
team = models.ForeignKey(Team, blank=True, null=True)
member = models.ForeignKey(TeamMember, blank=True, null=True)
notes = models.TextField()
def __unicode__(self):
if self.team:
return "Team Form Record: " + unicode(self.team)
if self.member:
return "Member Form Record: " + unicode(self.member)
Essentially, I want it to have a relationship with team or a relationship with member, but not both. Is there a way to enforce this?
I can only see two viable solutions. First is actually the same as #mariodev suggested in the comment which is to use Genetic foreign key. That will look something like:
# make sure to change the app name
ALLOWED_RELATIONSHIPS = models.Q(app_label = 'app_name', model = 'team') | models.Q(app_label = 'app_name', model = 'teammember')
class TeamFormNote(models.Model):
content_type = models.ForeignKey(ContentType, limit_choices_to=ALLOWED_RELATIONSHIPS)
relation_id = models.PositiveIntegerField()
relation = generic.GenericForeignKey('content_type', 'relation_id')
What that does is it sets up a generic foreign key which will allow you to link to any other model within your project. Since it can link to any other model, to restrict it to only the models you need, I use the limit_choices_to parameter of the ForeignKey. This will solve your problem since there is only one generic foreign key hence there is no way multiple relationships will be created. The disadvantage is that you cannot easily apply joins to generic foreign keys so you will not be able to do things like:
Team.objects.filter(teamformnote_set__notes__contains='foo')
The second approach is to leave the model as it and manually go into the database backend and add a db constaint. For example in postgres:
ALTER TABLE foo ADD CONSTRAINT bar CHECK ...;
This will work however it will not be transparent to your code.
This sounds like a malformed object model under the hood...
How about an abstract class which defines all common elements and two dreived classes, one for team and one for member?
If you are running into trouble with this because you want to have both referenced in the same table, you can use Generic Relations.
Let's say there is a model with default ordering described in Meta class
class People(models.Model):
first_name = models.CharField(max_length=100)
last_name = models.CharField(max_length=100)
middle_name = models.CharField(max_length=100)
class Meta:
ordering = (last_name, first_name)
Is there are a way to get unordered queryset
You can just override by simply adding .order_by()
for example:
People.objects.all().order_by()
This will make sure that Meta ordering is overridden.
Using an empty order_by() worked for queries with filters.
BUT asking for the .first() element was problematic and always resulted in ORDER BY.
To overcome this what I did was a little bit ugly:
People.objects.all().order_by()[:1].values_list(...)[0]
This effectively removes the ORDER BY, and avoids the problem that asking for [:1][0] where it would get optimized and add it anyway. At the moment I don't know about other workarounds, but I'd love to hear them. I tried doing the above code without the values_list, but in that case Django always tried to perform the Order By.
I Have two classes: Story and XmlLink. And there is many-to-many relationship between them.
I'm trying to customize saving method of XmlLink.
Idea: when I added new xmllink-object, a new story-object (temp) creates some records in db. But also I need to customize .story in def save to replace m-to-m relations with, for example, (6,7)
class Story(models.Model):
title = models.CharField(max_length=100)
content = models.TextField()
class XmlLink (models.Model):
title = models.CharField(max_length=150)
story = models.ManyToManyField(Story)
pk = 1
def save(self):
**self.story = (6,7)**
temp = Story(title = 'temp',
content = 'testtttt',
temp.save()
super(XmlLink, self).save()
i'm tried to search some examples, and different ways to solve this, but ...
Maybe someone can simplify the procedure?
This may be of some help in how to save a M2M relationship in django: https://docs.djangoproject.com/en/dev/topics/db/examples/many_to_many.
I find that it's usually more straightforward and easier to save it directly in the view, rather than defining a save method on the model.
Also, this answer may be of help: Creating a model object with a M2M and FK relationship.
I have two models in Django linked together by ManyToMany relation like this:
class Person(models.Model):
name = models.CharField(max_length=128)
class Group(models.Model):
name = models.CharField(max_length=128)
members = models.ManyToManyField(Person)
I need to get the main person in the group which is the first person in the group. How can I get the first person?
Here's how I'm adding members:
grp = Group.objects.create(name="Group 1")
grp.save()
prs = Person.objects.create(name="Tom")
grp.members.add(prs) #This is the main person of the group.
prs = Person.objects.create(name="Dick")
grp.members.add(prs)
prs = Person.objects.create(name="Harry")
grp.members.add(prs)
I don't think I need any additional columns as the id of the table group_members is a running sequence right.
If I try to fetch the main member of the group through Group.objects.get(id=1).members[0] then Django says that the manager is not indexable.
If I try this Group.objects.get(id=1).members.all().order_by('id')[0], I get the member with the lowest id in the Person table.
How can I solve this?
Thanks
Django doesn't pay attention to the order of the calls to add. The implicit table Django has for the relationship is something like the following if it was a Django model:
class GroupMembers(models.Model):
group = models.ForeignKey(Group)
person = models.ForeignKey(Person)
Obviously, there's nothing there about an "order" or which you should come first. By default, it will probably do as you describe and return the lowest pk, but that's just because it has nothing else to go off of.
If you want to enforce an order, you'll have to use a through table. Basically, instead of letting Django create that implicit model, you create it yourself. Then, you'll add a field like order to dictate the order:
class GroupMembers(models.Model):
class Meta:
ordering = ['order']
group = models.ForeignKey(Group)
person = models.ForeignKey(Person)
order = models.PositiveIntegerField(default=0)
Then, you tell Django to use this model for the relationship, instead:
class GroupMembers(models.Model):
group = models.ForeignKey(Group)
person = models.ForeignKey(Person, through='GroupMembers')
Now, when you add your members, you can't use add anymore, because additional information is need to complete the relationship. Instead, you must use the through model:
prs = Person.objects.create(name="Tom")
GroupMembers.objects.create(person=prs, group=grp, order=1)
prs = Person.objects.create(name="Dick")
GroupMembers.objects.create(person=prs, group=grp, order=2)
prs = Person.objects.create(name="Harry")
GroupMembers.objects.create(person=prs, group=grp, order=3)
Then, just use:
Group.objects.get(id=1).members.all()[0]
Alternatively, you could simply add a BooleanField to specify which is the main user:
class GroupMembers(models.Model):
group = models.ForeignKey(Group)
person = models.ForeignKey(Person)
is_main_user = models.BooleanField(default=False)
Then, add "Tom" like:
prs = Person.objects.create(name="Tom")
GroupMembers.objects.create(person=prs, group=grp, is_main_user=True)
And finally, retrieve "Tom" via:
Group.objects.get(id=1).members.filter(is_main_user=True)[0]
Relational database don't have any notion of ordering, by default. There's no such thing as "first". You need to add an explicit "order" field which keeps the order, and order by it.
You were pretty close:
Group.objects.get(id=1).members.all()[0]
(But as Alex Gaynor says in his answer, for predictable behaviour you need a proper field to sort on)
to reliably keep a record of who is the "main" person in a group, you may want to have a look at using a through table to keep this metadata