Partial model inheritance in Django - django

I have a model that I'm using as a reference point to create another model:
class ImYourFather(models.Model):
force = fields.HTMLField(null=True, blank=True)
supermarket_planets = models.HTMLField(null=True, blank=True)
destroying_planets = models.HTMLField()
class Luke(ImYourFather):
# Inheriting all the fields from father
the_cool_kid = models.HTMLField() # extra field added
I DO NOT want to inherit the destroying_planets field, Is this possible?
I'm asking specifically because destroying_planets is supposed to be mandatory in the father model but I'd like to have it optional in the child model.
Is this achievable in another way?

You can't partially inherit but you can manually create the models with whatever fields you need. This is not exactly the same as multi-table inheritance but that is the only way to partially inherit fields:
class Base(models.Model):
force = fields.HTMLField(null=True, blank=True)
supermarket_planets = models.HTMLField(null=True, blank=True)
class Meta(object):
abstract = True
class ImYourFather(Base):
destroying_planets = models.HTMLField()
class Luke(Base):
# optional in case you need this
father = models.OneToOneField(ImYourFather, related_name='lukes')
the_cool_kid = models.HTMLField() # extra field added
edit
Another approach is to simply copy the fields from the father. This is all untested so Django might bark at you for some of it). The advantage is that no monkey-patching but should work:
exclude = ['id', 'destroying_planets']
try: # Django 1.7
fields = {i.attname: i.clone() for i in ImYourFather._meta.fields if not i.attname in exclude}
except AttributeError: # Django < 1.7
fields = {i.attname: deepcopy(i) for i in ImYourFather._meta.fields if not i.attname in exclude}
Base = type('Base', (models.Model,), fields)
class Luke(Base):
# optional in case you need this
father = models.OneToOneField(ImYourFather, related_name='lukes')
the_cool_kid = models.HTMLField() # extra field added

Related

Queryset for specific child class (NOT abstract model)

How do I get a queryset of specific child classes? It doesn't look like going through related_name is the answer. Using Django 3.2.6 btw.
Here is my setup:
class Quiz(models.Model):
name = # char field
class Question(models.Model):
# a quiz has many questions
quiz = models.ForeignKey(Quiz, related_name = '%(class)s')
# etc...
class TFQuestion(Question):
is_true = models.BooleanField(...)
class MCQuestion(Question):
n_choices = models.IntegerField(...)
What I want is to get a queryset of just MC Questions and process them as such.
I feel the need to emphasize that Question is NOT abstract. So the question model has its own related_name but the specified '%(class)s' pattern does not seem to propagate down to the child classes. All the other threads I've seen suggest using that pattern but it only works for abstract models! not when using multi-table inheritance.
From what I've seen, I can't just do:
quiz1 = Quiz.objects.get(id=1)
# this works; returns queryset of Question objects
allquestions = quiz1.question.all()
# doesn't work
all_mcqs = quiz1.mcquestion.all()
You can .filter(…) [Django-doc] with:
MCQuestion.objects.filter(quiz=quiz1)
For inheritance of a concrete model, Django will make tables for the Question, TFQuestion and MCQuestions, so three tables. The MCQuestion will have a "hidden" OneToOneField to Question.
self answer (kinda)
a couple workarounds i've seen, but jankier than just using a related_name:
A.) using classname__isnull:
quiz1 = Quiz.objects.get(id=1)
# get quiz1's MCQuestions as Questions
qs1 = quiz1.question.filter(mcquestion__isnull = False)
# returns: <QuerySet[<Question>,...]>
qs1.first().mcquestion.n_choices
B.) using InheritanceManager and classname__isnull:
from model_utils.managers import InheritanceManager
class Question(models.Model):
# ...
objects = InheritanceManager()
# django shell
quiz1 = Quiz.objects.get(id=1)
# get quiz1's MCQuestions as MCQuestions
qs1 = quiz1.question.select_subclasses().filter(mcquestion__isnull = False)
# returns: <InheritanceQuerySet[<Question>,...]>
qs1.first().n_choices

Django: To combine and retrieve rows from different models

1.We have two similar models, and
I would like to be able to retrieve these at the same time and sort them by posting date and time, etc.
Is it possible?
2.Or should both redundant fields be combined into one model?
# 1.
class Model1(models.Model):
title = ...
thumbnail = ...
description = ...
...
class Model2(models.Model):
title = ...
image_url = ...
product_id = ...
reivew = ...
# 2. Combine into one model
class Model(models.Model)
title = ...
thumbnail = ...
description = ...
image_url = ...
product_id = ...
reivew = ...
You can union() two models with no shared parents, and after that order_by() some
column.
'Consider this answer from a beginner'
If you just want to fetch someones model1 and model2 objects with just one query statement maybe is good to inherit two models from a base model like this:
in your models.py:
class Base(models.Model):
title = ...
created_at = models.DateTimeField(auto_now_add=True)
owner = models.ForeignKey(user)
class Meta:
ordering = ['-created_at']
# inherits fields of Base model
class Model1(Base):
field = ...
# fields
class Model2(Base):
# fields
using this method, remember fill needed fields of `Base' model like this:
>>> m1 = Model1(field="value", title=..., owner=UserObject,...).save()
# for get objects do normal:
>>> objects = Base.objects.filter(owner=user)
# the result is list of users `Model1' or 'Model2` created objects
Also you can use django-model-utils and get Base objects by child type. It would be like this:
from model_utils.managers import InheritanceManager
class Base(models.Model):
# like previous version
# just remember modify objects
objects = InheritanceManager()
# inherits fields of Base model
class Model1(Base):
class Model2(Base):
get objects:
>>> Base.objects.all().select_related('Model1', 'Model2')
please read also this answer and others.

Django inline formset multiple models

TL;DR: I need a some kind of formset for formsets.
I have two different models related to one buisness-entity, and I need to make a form to edit both models like a one form. And I need to create a lot of such forms on the one page like Django inline formset does.
Now I have the following thing:
class Parent(models.Model):
name = models.Charfield()
class FirstChild(models.Model):
name = models.Charfield()
e_id = models.IntegerField()
parent = models.ForeignKey(Parent)
class FirstChildForm(django.forms.ModelForm):
class Meta:
model = Child
fields = ('name', 'e_id', 'parent')
widgets = {'parent': forms.TextInput}
And I render a lot of them using inline formsets:
formset_class = inlineformset_factory(Parent, FirstChild,
form=FirstChildForm, extra=1)
But now I have to add second child model and a form for it, and still render it like an one inline form, but make it form actually edit two models. Like this:
class SecondChild(models.Model):
name = models.Charfield()
e_id = models.IntegerField()
parent = models.ForeignKey(Parent)
class SecondChildForm(django.forms.ModelForm):
class Meta:
model = Child
fields = ('name', 'e_id', 'parent')
widgets = {'parent': forms.TextInput}
formset_class = inlineformset_factory(models=[Parent, FirstChild],
forms=[FirstChildForm, SecondChildForm],
extra=1)
As far as I understand, Django formsets cannot work with multiple models right now.
So which way should I choose to implement this behaviour and do not broke all django conceptions?, I cannot use some extra libraries so I have to implement everything by myself and I use django 1.6 if it is important.
So, finally I used this approach as a base: https://micropyramid.com/blog/how-to-use-nested-formsets-in-django/

Serialize relation both ways with Django rest_framework

I wonder how to serialize the mutual relation between objects both ways with "djangorestframework". Currently, the relation only shows one way with this:
class MyPolys(models.Model):
name = models.CharField(max_length=20)
text = models.TextField()
poly = models.PolygonField()
class MyPages2(models.Model):
name = models.CharField(max_length=20)
body = models.TextField()
mypolys = models.ManyToManyField(MyPolys)
# ...
class MyPolysSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = testmodels.MyPolys
class MyPages2Serializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = testmodels.MyPages2
# ...
class MyPolyViewSet(viewsets.ReadOnlyModelViewSet):
queryset = testmodels.MyPolys.objects.all()
serializer_class = srlz.MyPolysSerializer
class MyPages2ViewSet(viewsets.ReadOnlyModelViewSet):
queryset = testmodels.MyPages2.objects.all()
serializer_class = srlz.MyPages2Serializer
The many-to-many relation shows up just fine in the api for MyPages2 but nor for MyPolys. How do I make rest_framework aware that the relation goes both ways and needs to be serialized both ways?
The question also applies to one-to-many relations btw.
So far, from reading the documentation and googling, I can't figure out how do that.
Just do it like this:
class MyPolysSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = testmodels.MyPolys
fields =('id','name','text','poly')
class MyPages2Serializer(serializers.HyperlinkedModelSerializer):
mypolys = MyPolysSerializer(many=True,read_only=True)
class Meta:
model = testmodels.MyPages2
fields =('id','name','body','mypolys')
I figured it out! It appears that by adding a mypolys = models.ManyToManyField(MyPolys) to the MyPages2 class, Django has indeed automatically added a similar field called mypages2_set to the MyPolys class, so the serializer looks like this:
class MyPolysSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = testmodels.MyPolys
fields = ('name', 'text', 'id', 'url', 'mypages2_set')
I found out by inspecting an instance of the class in the shell using ./manage.py shell:
pol = testmodels.MyPolys.objects.get(pk=1)
pol. # hit the tab key after '.'
Hitting the tab key after the '.' reveals additional fields and methods including mypages2_set.

How to add model field dynamically?

I want to specify a model field dynamically in models.py. For example:
class ModelA(models.Model):
pass # whatever
# immediately after class declaration in models.py
if django_is_not_non_rel:
MyModel.all_modelb = models.ManyToManyField(ModelB, through="ModelAB")
But this code example does not work. When the condition is true, ModelA does not have the all_modelb field after everything is initialized. Why???? How do I effect my intention?
Creating columns dynamically is not impossible with Django but you'll have to do it using a metaclass and/or inheritance.
For your problem the easiest solution would probably be something like this:
if django_is_not_non_rel:
class ModelABase(models.Model):
MyModel.all_modelb = models.ManyToManyField(ModelB, through="ModelAB")
class Meta:
abstract = True
else:
class ModelABase(models.Model):
class Meta:
abstract = True
class ModelA(ModelABase):
pass # whatever
Or even simpler (if you don't need as much customization):
class ModelAORMBase(models.Model):
MyModel.all_modelb = models.ManyToManyField(ModelB, through="ModelAB")
class Meta:
abstract = True
if django_is_not_non_rel:
ModelABase = ModelAORMBase
else:
ModelABase = models.Model
class ModelA(ModelABase):
pass # whatever