The following is what I'm trying to do.
Of course this will be darn slow, and wonder if there's a better way of doing it.
class Foo(models.Model):
bars = generic.GenericRelation(Bar)
class Meta:
abstract = True
class Bar(models.Model):
content_type = models.ForeignKey(ContentType)
object_id = models.PositiveIntegerField()
invitation = generic.GenericForeignKey('content_type', 'object_id')
timestamp = models.DateTimeField(auto_now_add=True, db_index=True)
meat = models.ForeignKey(Jessy)
bars = Bar.objects.none()
for foo in Foo.objects.all():
bars = bars | Q(foo.bars.all())
bars.values('meat').order_by('timestamp'):
You can just use:
Bar.objects.filter(content_type=ContentType.objects.get_for_model(Foo), object_id__isnull=False).values('meat').order_by('timestamp')
That will query all Bar objects that have associated any Foo model.
Related
I have an Abstract base class "Parent" from which derive two subclasses "Child1" and "Child2". Each child can have a set of "Status".
I used "ContentType", "GenericForeignKey" and "GenericRelation" like so:
from django.db import models
from django.contrib.contenttypes.generic import GenericRelation, GenericForeignKey
from django.contrib.contenttypes.models import ContentType
class Parent(models.Model):
name = models.CharField(max_length=30, blank=True)
class Meta:
abstract = True
def __str__(self):
return self.name
class Child1(Parent):
id_camp = models.PositiveIntegerField()
config_type = models.CharField(max_length=30)
status_set = GenericRelation(Status)
class Child2(Parent):
temperature = models.FloatField(null=True, blank=True)
status_set = GenericRelation(Status)
class Status(models.Model):
code = models.CharField(max_length=10, null=True, blank=True)
message = models.CharField(max_length=100, null=True, blank=True)
content_type = models.ForeignKey(ContentType, limit_choices_to={'name__in': ('child1', 'child2',)}, null=True, blank=True)
object_id = models.PositiveIntegerField()
content_object = GenericForeignKey('content_type', 'object_id')
The actual solution works fine, but right now the limitation of choices of content type is by "name" and eventually I will create more subclacess of Parent later. I would like to replace limit_choices_to={'name__in': ('child1', 'child2',)} with somthing like limit_choices_to children of parent is there any straightforward way ?
limit_choices_to also accepts callables, so yes, a dynamic value should be possible:
Either a dictionary, a Q object, or a callable returning a dictionary or Q object can be used.
So something along these lines should work:
def get_children():
return {'model__in': [c.__name__ for c in Parent.__subclasses__()]}
and then later ...
limit_choices_to=get_children
Or even in one line:
limit_choices_to={'model__in': [c.__name__ for c in Parent.__subclasses__()]}
Model "A" is a generic model, meaning any model can relate to it. Model "B" and "C" are models that want to establish a foreign key relation with Model "A". How can this be done?
class A(models.Model):
content_type = models.ForeignKey(ContentType)
object_id = models.PositiveIntegerField()
content_object = GenericForeignKey('content_type', 'object_id')
list = # What goes here?
class B(models.Model):
parent = # What goes here to refer to model A?
name = models.CharField(max_length=255)
age = models.IntegerField()
class C(models.Model):
parent = # What goes here to refer to model A?
title = models.CharField(max_length=255)
body = models.TextField()
An example lookup would be:
A.list.all()
Giving me a list of either B objects or C objects.
This is completely the wrong design for what you are asking for. Your structure only allows one single item to be related to each A, whether it is a B or a C.
Instead you need an intermediate model, which contains the GenericForeignKey and which also has a (normal) ForeignKey to A. That is how tagging applications work: see for example django-tagging's TaggedItem model.
I've decided to use different related names which works in my case:
class A(models.Model):
content_type = models.ForeignKey(ContentType)
object_id = models.PositiveIntegerField()
content_object = GenericForeignKey('content_type', 'object_id')
class B(models.Model):
parent = models.ForeignKey('A', related_name='B')
name = models.CharField(max_length=255)
age = models.IntegerField()
class C(models.Model):
parent = models.ForeignKey('A', related_name='C')
title = models.CharField(max_length=255)
body = models.TextField()
I have three models called Post, Category and Flag.
I let users to make a Flag on each Post which is tagged with a Category. Now I want to list all of Flags, related to Posts on a certain Category.
I've found two ways of doing it using ListView generic view:
First approach:
Define a generic relation between Flag and Category so I can call something like: Flag.objects.filter(object__categor=self.category)
Second approach:
def get_queryset(self):
pk = self.kwargs['pk']
self.category = get_object_or_404(Category, pk=pk)
flags = Flag.objects.all()
qs=[]
for f in flags:
if f.object.category == self.category:
qs.append(f)
return qs
My question is which approach is more efficient? I'm not familiar with generic relations, but it seems a little bit heavy to do. On the other hand, querying on all Flag objects and iterating through them isn't cheap one.
Models.py
class Flag(models.Model):
flagger = models.ForeignKey(User, related_name='flaggers')
content_type = models.ForeignKey(ContentType)
object_id = models.PositiveIntegerField()
object = GenericForeignKey('content_type', 'object_id')
submit_date = models.DateTimeField(auto_now_add=True)
reason = models.IntegerField(choices=REASON_CHOICES, default=BROKEN_LINK)
class Category(models.Model):
name = models.CharField(max_length=100)
slug = models.SlugField(unique=True)
...
class Base(models.Model):
title = models.CharField(max_length=200)
slug = models.CharField(max_length=200, db_index=True)
category = models.ForeignKey(Category, default=1)
...
class Text(Base):
description = models.TextField(max_length=500)
...
class Image(Base):
image = models.ImageField()
class Post(models.Model):
content_type = models.ForeignKey(ContentType)
object_id = models.PositiveIntegerField()
content_object = GenericForeignKey('content_type', 'object_id')
created_at = models.DateTimeField(null=True)
After saving each of Text or Image instances, I create a Post model. So I can get either Posts flagged or text flagged and image flagged separately.
[also any tip about how can I define generic relation in case I should use it, would be appreciated]
I have the following models:
Class A(models.Model):
content_type = models.ForeignKey(ContentType)
object_id = models.PositiveIntegerField()
content_object = generic.GenericForeignKey('content_type', 'object_id')
thumbnail = models.ImageField(...)
class B(models.Model)
title = models.CharField()
def save(*args, **kwargs):
# Based on the title field I want to fetch some picture and then save the thumbnail in A
I have more classes like B which should be referenced from A (this is why I use GenericForeignKey). The problem I am trying to figure out is how to save the thumbnail field (on A) when I am in the save() method in B. Inserting many if statement in A to check the type of the referenced class and save the thumbnail accordingly is pretty cumbersome.
Looking at the docs, you can add a reverse generic relation from B to A:
If you know which models you’ll be using most often, you can also add a “reverse” generic relationship to enable an additional API
class A_Model(models.Model):
content_type = models.ForeignKey(ContentType)
object_id = models.PositiveIntegerField()
content_object = generic.GenericForeignKey('content_type', 'object_id')
thumbnail = models.ImageField(...)
class B_Models(models.Model)
title = models.CharField()
a_models = generic.GenericRelation(A_Model)
and now you can do:
b = B_Model()
a = A_Model(content_object=b, thumbnail=...)
a.save()
b.a_models.all()
Say I have the following model:
class Foo(models.Model):
content_type = models.ForeignKey(ContentType)
object_id = models.PositiveIntegerField()
content_object = generic.GenericForeignKey('content_type', 'object_id')
class Bar(models.Model):
baz = models.BooleanField()
then run the following code:
f = Foo(content_object=Bar(baz=False))
print f.content_object
what I would expect to see is something like:
<Bar: Bar object>
but instead it seems as if it's empty... why is this?
Follow the following:
b=Bar(baz=False)
b.save()
f = Foo(content_object=b)
f.content_object
This gives the desired result for you.
Content_object has to be split into content_type and object_id. And until you save the object into the database there is no object_id available. Therefore you have to save it first - like Sandip suggested. You can do it in a shorter form as well: Baz.objects.create(baz=False)