I'm trying to achieve the following model structure:
class X(models.Model):
class Meta:
abstract = True
objects = InheritanceManager()
agroup = models.ForeignKey(A, related_name="%(class)s_set")
xfield = models.CharField()
class A(models.Model):
class Y(X):
yfield = models.CharField()
class Z(X):
zfield = models.CharField()
The first issue is, the Base X class can't be abstract it seems because I need to be able to iterate over all subclasses of X (Y,Y,Y,Z,Z) so I need access to the manager. While X is abstract, X.objects doesn't work.
Second issue is in the REST serializer. I can only reference x_set in ASerializer, as it is the only property that exists on A. And that only display the xfield in the nested list. What I would really like is y_set and z_set on the ASerializer with their respective yfield and zfield displayed.
I can achieve some of this with different configurations (iteration over children by removing abstract, or separation of children in the rest serializer by places the FK field on Y and Z directly), but never all the same time.
Thank you.
Phew!
I stuck with X being abstract, the way to then iterate over X's children:
for x_child_class in X.__subclasses__():
for child in x_child_class.objects.all():
#Do your stuff
And as long as X is abstract, Class A should have y_set and z_set on it, so you can just:
class ASerializer(serializers.HyperlinkedModelSerializer):
y_set = YSerializer(many=True)
z_set = ZSerializer(many=True)
class Meta:
model = A
Trivial, but took a long time for some reason.
Related
I am wondering since ages how to achieve the following:
I have an abstract class wrapping several attributes shared by multiple models. But not every model needs every attribute in exactly the same way.
Here is an example: MyModelA and MyModelB both have two fields: value_1 and value_2. But while MyModelA needs them to be required/not nullable, they can be nullable for MyModelB.
class MyAbstractModel(models.Model):
value_1 = models.IntegerField()
value_2 = models.IntegerField()
class Meta:
abstract = True
What I tried:
Not deriving MyAbstractModel from models.Model but from object like a regular mixin
Setting a class attribute which is overwritten in the child classes to determine the nullable-state. It always takes the definition from MyAbstractModel.
Using a class attribute but not set it in the MyAbstractModel. Then the makemigrations command fails because of the undefined variable.
Cry
Being DRY is such an important paradigm in django, I really wonder that there is nothing on this topic in the internet.
Thanks in advance!
So I'm struggling with ordering the choices within an InlinePanel (for an orderable) on my site. In the admin page, when adding a new item, the options are presented in the order they were added to the site (so, essentially the 'id' for that item); this is less than ideal considering there are hundreds of options presented in a manner that is not user friendly.
I'm assuming this needs to be defined as ordering meta within the orderable, but I can't seem to get it to work. This is what my orderable looks like:
class RelatedPeople(Orderable):
service = ParentalKey('service.Services', related_name='related_person')
person = models.ForeignKey('person.People', null=True, on_delete=models.SET_NULL, related_name='related_service')
panels = [
FieldPanel('person')
]
I've tried the following with no success:
class Meta:
ordering = 'person'
and, trying to append the field within 'person' that I want to sort by, 'name':
class Meta:
ordering = 'person.name'
There must be an obvious way to solve this that I'm over looking. A default sort order of the 'id' (in this case, for 'person.People') is rarely ever going to be suitable from the perspective of the content creator.
Any advice would be greatly appreciated!
Thanks in advance,
Rob
Person model should have:
ordering = ['name']
instead of
ordering = 'name'
And your Orderable object should have it's meta changed to
class Meta(Orderable.Meta):
Via Django Docs, this is the example of abstract base classes, and ordering:
Meta and multi-table inheritance¶
In the multi-table inheritance situation, it doesn’t make sense for a
child class to inherit from its parent’s Meta class. All the Meta
options have already been applied to the parent class and applying
them again would normally only lead to contradictory behavior (this is
in contrast with the abstract base class case, where the base class
doesn’t exist in its own right).
So a child model does not have access to its parent’s Meta class.
However, there are a few limited cases where the child inherits
behavior from the parent: if the child does not specify an ordering
attribute or a get_latest_by attribute, it will inherit these from its
parent.
If the parent has an ordering and you don’t want the child to have any
natural ordering, you can explicitly disable it:
class ChildModel(ParentModel):
# ...
class Meta:
# Remove parent's ordering effect
ordering = []
When an abstract base class is created, Django makes any Meta inner
class you declared in the base class available as an attribute. If a
child class does not declare its own Meta class, it will inherit the
parent’s Meta. If the child wants to extend the parent’s Meta class,
it can subclass it. For example:
from django.db import models
class CommonInfo(models.Model):
# ...
class Meta:
abstract = True
ordering = ['name']
class Student(CommonInfo):
# ...
class Meta(CommonInfo.Meta):
db_table = 'student_info'
I am not familiar with Wagtail, but can you take a look at this issue :
https://github.com/wagtail/wagtail/issues/4477#issuecomment-382277375
Update:
Maybe you just need to update your Person model like this:
class Person(models.Model):
...
class Meta:
ordering = 'name'
In your files, you try to order RelatedPeople by Person, but what you need is to order the Person list by name in your wagtail dropdown
Suppose there are total 3 class. A,B and C.
class A(models.Model):
one = models.IntegerField()
two = models.IntegerField()
three = models.IntegerField()
class Meta:
abstract = True
class B(A):
pass
class C(A):
pass
I am inheriting the class A in B and C,but i want to use only fields one and two in classB while all the three fields in classC.
Is it possible to inherit some fields of classA in classB and some in classC?
or is it a bad idea?
As you may already know, there are three types of inheritance across models in django.
Often, you will just want to use the parent class to hold information that you don’t want to have to type out for each child model. This class isn’t going to ever be used in isolation, so Abstract base classes are what you’re after.
If you’re subclassing an existing model (perhaps something from another application entirely) and want each model to have its own database table, Multi-table inheritance is the way to go.
Finally, if you only want to modify the Python-level behavior of a model, without changing the models fields in any way, you can use Proxy models.
The only choice for your use-case is abstract base classes.
And the thing you are looking for from docs:
Fields inherited from abstract base classes can be overridden with another field or value, or be removed with None.
So you should have:
class A(models.Model):
one = models.IntegerField()
two = models.IntegerField()
three = models.IntegerField()
class Meta:
abstract = True
class B(A):
three = None
class C(A):
three = None
And to answer your second question, It's not a bad idea; We normally use it when we want to change the USERNAME_FIELD while extending django's default user model.
I have a parent class which should be inherited by child classes that will become Django models. The child classes all have a common property x, so it should be an abstract property of the parent. The thing that differs between the children, is whether the property is a django model attribute or if it is directly set.
class A(Model, metaclass=abc.ABCMeta):
class Meta:
abstract = True
#property
#abc.abstractmethod
def x(self):
pass
class B(A):
x = "hello"
class C(A):
x = models.CharField(max_length=100, default='defaultC')
class D(A):
x = models.CharField(max_length=50, default='defaultD')
So in my example, for some child classes the property x is known, but in other cases it needs to be selected. But one certain thing is that all descendants of the class A need an x attribute as it is a common property that must be defined by any descendants.
The problem is that if I ever instantiate a class of C or D, I get the following:
TypeError: Can't instantiate abstract class C with abstract methods x
I think the issue is something behind the scenes with Django's metaclass. It removes any django model attributes and replaces them with deferred attributes, or something, and so the property is never actually defined.
I can try moving the model attribute up to class A and let the children class override as a static value as need be:
class A(Model, metaclass=abc.ABCMeta):
class Meta:
abstract = True
x = models.CharField(max_length=100)
class B(A):
x = "hello"
class C(A):
x = models.CharField(max_length=100, default='defaultC')
class D(A):
x = models.CharField(max_length=50, default='defaultD')
My issue with that is that this doesn't feel very abstract. C and D still need to override x to set their defaults and max length. It also worries me that people using class A generically in code will assume attribute x will be a django database field; when instead sometimes it can be a static string value.
What is the ideal approach here? Really I'd like to achieve my first example is possible.
I wonder if it's possible to get all the parents classes from the inner Meta class.
class Abc(A,B,C):
class Meta:
# I wanna know A,B,C without explicitly listing them again.
# something like self.parents()
Edit : The actual problem..
Tastypie accessing fields from inherited models
I needed to list parents classes in meta.
class Abc(A,B,C):
class Meta:
list_of_fields_of_parents = A.fields + B.fields + C.fields
You can use Abc.__bases__. For example:
class Meta:
list_of_fields_of_parents = [b.fields for b in Abc.__bases__]
Because this doesn't work for the Meta inside the Abc class, you can store your base classes in a list to keep things DRY:
abc_bases = [A, B, C]
class Abc(*bases):
class Meta:
list_of_fields_of_parents = [b.fields for b in abc_bases]