I have a super class like this:
class Superclass(models.Model):
number = models.PositiveIntegerField()
class Meta:
abstract = True
def get_next(self):
return Superclass.objects.get(number=self.number+1)
Now, I have a child class that inherits from the superclass.
What's the problem?
I can't do this: Superclass.objects because the superclass doesn't refer to any database table.
I don't want to query all Superclass childs, only the one of the current child class, like this: When I do instance_of_child1.get_next I don't want to get an instance of Child2.
How to solve this?
My first idea was to add a static constant to any child class that contains the class (So I could do self.myclass.objects) But this seems to be not a good way.
Make the method get_next being part of the child class. Problem: there will be duplicates.
This should work:
def get_next(self):
return self.__class__.objects.get(number=self.number+1)
Related
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.
Let me explain. I have 2 tables which are child classes of another abstract table. The abstract table has a relationship to a model called Foo. The related_name is set dynamically. The code looks like this:
class Foo(models.Model):
...
class Parent(models.Model):
foo = models.ForeignKey(
Foo,
on_delete=models.CASCADE,
related_name='%(app_label)s_%(class)s_related'
)
...
def bar(self):
print('bar')
class Meta:
abstract = True
class ChildOne(Parent):
...
class ChildTwo(Parent):
...
Therefore, the related names become 'myapp_childone_related', and 'myapp_childtwo_related'.
Now, lets say I want to call the bar() method of all the objects from the ChildOne and ChildTwo model that is related to a Foo object. There is a catch though, I want to it from with a class method of the Foo model. Currently, I'm doing it like this:
class Foo(models.Model):
...
def call_bar(self):
references = ('childone', 'childtwo')
for ref in references:
children = getattr(self, f'myapp_{ref}_related').all()
for child in children:
child.bar()
This works fine, but honestly feels a bit hack-y, especially when dealing with more than two children classes. Is there a nicer, more Pythonic solution to this problem?
Edit: I decided not to mention previously that I wanted to call the bar() method from within a class method of the Foo model because I thought that it was unnecessary for this question. However, Daneil Roseman's answer suggested making a list of classes, which is a good solution, but it would not work within the class method, as the classes have not yet been defined at that point in the module. So mentioning that in this edit.
A related_name is only syntactic sugar for performing a query from the related class itself. So you should just do this explicitly:
child_classes = [ChildOne, ChildTwo]
for child_class in child_classes:
children = child_class.objects.filter(foo=foo)
Let's say I have a parent class (Products) and child class related by OneToOneField. Now let's say I have a Products instance object. How will I be able to access all the fields of child ?
class Products(models.Model):
......
class Child1(models.Model):
parent=models.OneToOneField(Products)
......
class Child2(models.Model):
parent=models.OneToOneField(Products)
......
Now let
product_instance=Products.objects.get(id=id)
How can I access child fields without knowing the child class name?
There is package called DeepCollector using this you can get all related objects
from deep_collector.core import DeepCollector
collector = DeepCollector()
collector.collect(Products)
related_objects = collector.get_collected_objects()
try this...
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.
I have the following code:
# apps/models.py :
class Parent(models.Model):
name = models.CharField(max_length=80)
def __unicode__(self):
clist = ", ".join([x.__unicode__() for x in self.children.all()])
return self.name + clist
class Child(models.Model):
unit = models.ForeignKey(Parent, related_name='children')
desc = models.CharField(max_length=80)
def __unicode__(self):
return self.desc
class ChildA(Child):
text = models.TextField()
def __unicode__(self):
return self.text[:40]
I have several items of type ChildA. Why when I ask for a __unicode__() of the relevant Parent, the string I get in return is the one generated by the __unicode__() method of Child and not the __unicode__() method of ChildA ?
Updates:
This is standard behavior. Another possible solutions in addition to answers below is an inheritance cast
This is standard behavior for inheritance. Parent is related directly with Child, not ChildA. When you call some_parent.children.all(), you get back a queryset of Child instances, so obviously, when you call unicode on one of those, it's calling Child.__unicode__.
UPDATE
There's not really a good way to get from a parent to the child. If you're using MTI (Multiple Table Inheritance), you can take advantage of the way Django implements it, namely as a OneToOneField with the parent. Because of that, there's also a reverse relationship on the parent to the child, but you must specifically test for it. For example:
class Child(models.Model):
...
def get_child(self):
if hasattr(self, 'childa'):
return self.childa
if hasattr(self, 'childb'):
return self.childb
...
It's not ideal by any means, and you need to be careful to always update this method whenever you subclass Child, which pretty much totally violates abstraction in OOP.
You probably want an abstract base class.
Read up on the difference here...
https://docs.djangoproject.com/en/dev/topics/db/models/#abstract-base-classes
Why when I ask for a unicode() of the relevant parent the string I
get in return is the one generated by the unicode() method of
Child ?
It follows that you are not calling the method on a Parent instance. That is why you are seeing this behaviour.
You can access the parent class's method by using the super() keyword:
a = ChildA()
#This will give you the normal method:
print unicode(a)
#super() will give you the parent's method:
super(ChildA, a).__unicode__()
You cannot simply use the unicode() function with the latter call, as super returns a proxy to the object, rather than an object in its own right.
This is not a good way to code. Leave it up to the class to override behaviour as it sees fit.