Django inner join to a model without directly relationship - django

i need to join a table without a direct relationship.
models.py:
class FooModel():
bar = ForeignKey(Bar)
class BarModel():
pass
class BazModel():
bar = ForeignKey(Bar)
class QuxModel():
foo = ForeignKey(Foo)
tried to reach Foo from Baz but didn't work
viewset.py:
def BazView(viewsets.ModelViewSet):
queryset = model.BazModel.objects.all().prefetch_related('bar').prefetch_related('baz__bar')
serializer_class = serializer.Baz
def get_queryset(self):
return self.queryset
serializer.py
class FooSerializer(serializer.ModelSerializer):
class Meta:
model = FooModel
exlude = []
class BarSerializer(serializer.ModelSerializer):
class Meta:
model = BarModel
exlude = []
class BazSerializer(serializer.ModelSerializer):
foo = FooSerializer()
class Meta:
model = BarModel
exlude = []
class QuxSerializer(serializer.ModelSerializer):
class Meta:
model = QuxModel
exlude = []
using prefetch like that i got an error saying that Baz has no foo field.
also would like to get data from QuxModel based on Foo FK...
how could i perform this?

Try this:
model.BazModel.objects.all().select_related('bar').prefetch_related('bar__foomodel_set')
And then in BazSerializer, you can set the source for foo like this:
class BazSerializer(serializer.ModelSerializer):
foo = FooSerializer(source='bar.foomodel_set')
This will tell the foo serializer to get it's data from the FooModel objects using BazModel's bar.
You might have to change foomodel_set to the related name you have set to access FooModel from BarModel.

Related

Get "Foo" queryset of ForeignKey relationships for initial "Bar" queryset?

I have a simple ForeignKey relationship:
class Foo(models.Model):
id = UUIDField()
class Bar(models.Model):
id = UUIDField()
foo = ForeignKey(foo)
If I have an initial queryset of Bar objects, how can I get a queryset of related Foo object for each respective Bar?
I'm currently doing this but I'm wondering if there's a better way:
bar_qs = Bar.objects.all().select_related("foo")
foo_ids = []
for i in bar_qs:
foo_ids.append(i.foo.id)
foo_qs = Foo.objects.filter(id__in=foo_ids)
Try this query:
Foo.objects.filter(bar_set__in=Bar.objects.all())
You're doing the right thing in using select_related because that gathers the related foo object in the same query so there's no need to query the database again for the Foo table.
If a list of foos is ok, you could just do;
bar_qs = Bar.objects.all().select_related("foo")
foos = []
for i in bar_qs:
foos.append(i.foo)
Or you could just start with the foo queryset using;
foo_qs = Bar.objects.select_related('foo').only('foo')
But if you need a bar_qs, then you'd be best doing;
bar_qs = Bar.objects.all().select_related("foo")
foo_qs = bar_qs.only('foo')

How to automatically choose related model using field value

Assume, we have model
class BaseModel(models.Model):
is_a = models.BooleanField()
and two models related to this one:
class A(models.Model):
value_1 = models.IntegerField()
base = models.ForeignKey(BaseModel, related_name='a')
class B(models.Model):
value_1 = models.IntegerField()
value_2 = models.IntegerField()
base = models.ForeignKey(BaseModel, related_name='b')
What I need is to refer to A or B depending on is_a property.
For example,
base = BaseModel.objects.get(id=1)
if base.is_a:
obj = A.objects.create(value_1=1, base=base)
else:
obj = B.objects.create(value_1=1, value_2=2, base=base)
return obj
or
if base.is_a:
queryset = base.a.all()
else:
queryset = base.b.all()
return queryset
i.e., every time I have to check the is_a property.
Is there more graceful way?
There are two only related models, A and B, no other ones will appear in the nearest future.
Part of the problem can be solved with django-polymorphic, e.g.:
class A(PolymorphicModel):
...
class B(A):
...
This allows to retrieve all A's and B's with one request like base.b.all(), but the problem here is that every B creates instance of A, which is unwanted.
I've considered GenericForeignKey as well. As far as I understood it has a number of limitations like "1) You can't use GenericForeignKey in query filters ; 2) a GenericForeignKey won't appear in a ModelForm" (from GenericForeignKey or ForeignKey).
One idea is to add choices to the BaseModel to have a string representation of your boolean value. If you set the strings equal to the A and B model names, you can use the model.get_foo_display() method to return the name of the model. Then use the Python getattr() method to access attributes as variables.
class BaseModel(models.Model):
base_model_choices = (
(True, 'A'),
(False, 'B'),
)
is_a = models.BooleanField(choices=base_model_choices)
For example,
base = BaseModel.objects.get(id=1)
queryset = base.getattr(models, get_is_a_display()).all()
obj = getattr(models, get_is_a_display()).objects.create(base=base)

Is it possible to have an abstract model inside another abstract model in Django/Djongo?

I'm trying to add a model class with abstract=True in meta to another abstract class.For example,
class MainRecord(models.Model):
date = models.DateTimeField(auto_now_add=True,null=True)
X_data = models.EmbeddedField(
model_container=X,
model_form_class= X_Form,
)
class X(models.Model):
HRCT = models.BooleanField()
Y = models.EmbeddedField(
model_container=Y,
model_form_class=Y_Form,
)
class Meta:
abstract = True
class Y(models.Model):
Y_present = models.BooleanField()
Location = models.EmbeddedField(
model_container=Location,
model_form_class=Location_Form,
)
class Meta:
abstract = True
In Django admin, I'm able to see the embedded fields and it's corresponding checkboxes. I'm able to add/update values for the boolean field HRCT (Embedded from class X) but I'm not able to save the values/updates in the fields embedded from class Y to class X.

Dynamically creating google app_engine ndb objects

I am trying to use google's ndb model, adding some auto fields and definitions prior to model definition. The below code works well. My question is, though, any specific ndb model implementation is not used ( given I will be destroyed, if google changes anything) do you see any issue with portability of below
class MetaModel(type):
def __new__(cls,name,bases,attrs):
super_new = super(MetaModel,cls).__new__
if name == "Model":
return super_new(cls,name,bases,attrs)
if attrs.get('auto_date_time',True):
attrs['date_add'] = ndb.DateTimeProperty(auto_now_add= True)
attrs['date_upd'] = ndb.DateTimeProperty(auto_now= True)
attrs['_get_kind'] = classmethod(get_kind)
attrs['__name__'] = name
attr_meta = attrs.get('Meta',None)
if attr_meta is None:
meta = type('meta',(object,),dict())
else:
meta = attr_meta
kwargs= {}
model_module = sys.modules[attrs['__module__']]
kwargs['app_label'] = model_module.__name__.split('.')[-2]
_meta = Options(meta,name,**kwargs)
attrs['_meta'] = _meta
return type(name,(ndb.Model,),attrs)
class Model(object):
__metaclass__ = MetaModel
class TesTModel(Model):
name = ndb.StringProperty(indexed=False)
tm = TestModel(name='This is the test model')
tm.put()
This seems pretty fragile. Sounds like an expando model might work for you?
Edit (based on clarification below): metaclasses and type really should be a last resort. They're confusing and hard to get right. For example, in your code snippet, subclasses of Model get ndb's metaclass instead of the one above:
class T1(Model):
pass
>>> T1().to_dict()
T1 {'date_upd': None, 'date_add': None}
class T2(Model):
auto_date_time = False
class T3(T2):
auto_date_time = True
>>> T2.__metaclass__
<class 'ndb.model.MetaModel'>
>>> T3().to_dict()
{}
You can avoid some craziness by deriving your metaclass from ndb.MetaModel:
class MyMetaModel(ndb.MetaModel):
def __new__(cls, name, bases, attrs):
return super(MyMetaModel,cls).__new__(cls, name, bases, attrs)
class MyModel(ndb.Model):
__metaclass__ = MyMetaModel
class AutoTimeModel(MyModel):
date_add = ndb.DateTimeProperty(auto_now_add=True)
date_upd = ndb.DateTimeProperty(auto_now=True)

Mongoengine reference another document's field

Is it possible to do something like this?
class Doc1:
fieldd1 = StringField()
class Doc2:
fieldd2 = ReferenceField(Doc1.fieldd1)
Or should I just reference the Doc and then get the field information whenever I need it
This not posible and it is reference to document. To get fieldd1 you must do:
class Doc1(Document):
fieldd1 = StringField()
class Doc2(Document):
fieldd2 = ReferenceField(Doc1)
Doc2.objects.first().fieldd2.fieldd1
If you want just include document to another as part of one document then look at EmbeddedDocument and EmbeddedDcoumentField:
class Doc1(EmbeddedDocument):
fieldd1 = StringField()
class Doc2(Document):
fieldd2 = EmbeddedDcoumentField(Doc1)
Doc2.objects.first().fieldd2.fieldd1
But you always can set own properties:
class Doc1(Document):
fieldd1 = StringField()
class Doc2(Document):
fieldd2 = ReferenceField(Doc1)
#property
def fieldd1(self):
return self.fieldd2.fieldd1
Doc2.objects.first().fieldd1
See documentation: https://mongoengine-odm.readthedocs.org/en/latest/guide/defining-documents.html.