In my Django 1.11 app, I have an abstract model class AbstractModel, from which I subclass concrete models FooModel and BarModel. There are also concrete models FooValue and BarValue defined. From within AbstractModel, I want to define a one to one field to the corresponding Value model.
class AbstractModel(models.model)
value = models.OneToOneField('corresponding Value class name', ...)
class Meta:
abstract = True
class FooModel(AbstractModel):
pass
class BarModel(AbstractModel):
pass
class FooValue(models.model):
pass
class BarValue(models.model):
pass
How do I get corresponding 'XXXValue' class name depending on which concrete XXXModel class is in scope?
To do this, I need the current concrete model class name _my_class_name in the class definition (which seems hard to impossible).
Related
class BaseModel(models.Model): # base class should subclass 'django.db.models.Model'
creation_date = models.DateTimeField(..) # define the common field1
class Meta:
abstract=True # Set this model as Abstract
Inherit this Base class in models
After creating the abstract base class BaseModel, I inherited this class in my models.
class MyModel1(BaseModel): # inherit the base model class
# define other non-common fields here
...
After creating an object of class 'MyModel1', I want the 'creation_date' field to be shown in admin interface.
So that I can see the datetime when an object of class 'MyModel1' is created.
Solution:
class MyModel1Admin(admin.ModelAdmin):
readonly_fields= ['creation_date',]
admin.site.register(MyModel1,MyModel1Admin)
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.e. we have SomeSeries with several SomeDecors, where ForeignKey of SomeDecor points to SomeSeries. I want both to be abstract and later instantiate several pairs of it (with it's own tables in db). Is it possible?
I.e.
class SomeSeries(models.Model):
class Meta:
abstract = True
vendor = models.ForeignKey(Vendor)
name = models.CharField(max_length=255, default='')
def __unicode__(self):
return "{} {}".format(self.vendor, self.name)
class SomeDecor(WithFileFields):
class Meta:
abstract = True
series = models.ForeignKey(SomeSeries) # some magic here to make ForeignKey to abstract model
texture = models.ImageField()
# -------------------------------------------
class PlinthSeries(SomeSeries): pass
class PlinthDecor(SomeDecor): pass
# Some magic to make PlinthDecor.series points to PlinthSeries
EDIT
Actually I don't want complicity of polymorphic relations, I want pure abstract models just to save typing (what abstract models are initially for). Suppose in my case the simplest way is to exclude ForeignKey from base model and type it only in all inherited models:
class SomeSeries(models.Model):
class Meta:
abstract = True
#...
class SomeDecor(WithFileFields):
class Meta:
abstract = True
series = None #?
#..
texture = models.ImageField()
def do_anything_with_series(self): pass
class PlinthSeries(SomeSeries): pass
class PlinthDecor(SomeDecor): pass
series = models.ForeignKey(PlinthSeries)
You can't create ForeignKey referencing abstract model. It's, even, doesn't make any sense, because ForeignKey translates into Foreign Key Constraint which have to reference existing table.
As a workaround, you can create GenericForeignKey field.
You can not do it because if you create two class inherit from your abstract class to what class your foreignkey should do? for first or for second?
So you need to create GenericForeignKey or not do any field and only after create model inherits from your abstract model add your foreign key.
I am trying to design an abstract model that contains a field. Subclassed models will have this field, but they will be of various field types.
Example
class AbsModel(models.Model):
data = models.??? #I want subclasses to choose this
def __unicode__(self):
return data.__str__()
class Meta:
abstract = True
class TimeModel(AbsModel):
data = models.TimeField()
...
class CharModel(AbsModel):
data = models.CharField(...)
...
I am looking for a way to enforce the existence of the data field so I can write unicode once for all objects.
If this isn't possible, how can I refer to the "data" field of the subclass when calling the super class's unicode
I have a feeling this second question has an obvious answer I am missing.
It's not possible to override a superclass field where the field is of type models.Field.
https://docs.djangoproject.com/en/1.4/topics/db/models/#field-name-hiding-is-not-permitted
You can get round this by defining a field of another type in the superclass, and then overriding it in the child (perhaps include a __str__() method just in case the data field isn't overriden).
from django.db import models
class AbsDataField:
def __str__(self):
return "undefined"
class AbsModel(models.Model):
data = AbsDataField
def __unicode__(self):
return self.data.__str__()
class Meta:
abstract = True
class TimeModel(AbsModel):
data = models.TimeField()
#...
class CharModel(AbsModel):
data = models.CharField(max_length=32)
#...
You can write something like that:
class AbsModel(models.Model):
def __unicode__(self):
if hasattr(self, "data") and isinstance(self.data, models.Field):
return data.__str__()
return u"Unknown"
You cannot do that in Django:
In normal Python class inheritance, it is permissible for a child
class to override any attribute from the parent class. In Django, this
is not permitted for attributes that are Field instances (at least,
not at the moment). If a base class has a field called author, you
cannot create another model field called author in any class that
inherits from that base class.
Overriding fields in a parent model leads to difficulties in areas
such as initializing new instances (specifying which field is being
initialized in Model.__init__) and serialization. These are features
which normal Python class inheritance doesn't have to deal with in
quite the same way, so the difference between Django model inheritance
and Python class inheritance isn't arbitrary.
[...]
Django will raise a FieldError if you override any model field in any
ancestor model.
Given the following models:(don't mind the TextFields there're just for illustration)
class Base(models.Model):
field1 = models.TextField()
class Meta:
abstract=True
class Child1(Base):
child1_field = models.TextField()
class Child2(Base):
child2_field = models.TextField()
class Content(models.Model):
aso_items = models.ManyToManyField('Base')
According to these definitions a Content object can be associated with more than one Base object, eg. an interview(=Content object) can be linked with a musician(=Child1 object), a filmdirector(=Child2), etc.
Now, for my question:
Is it possible to filter Content objects according to which model the aso_items field points to?
An example : Say I would like a Queryset containing all the Content objects that are associated with a specific object of Child1(eg. all the interviews associated with the musician Bob Dylan), how can I achieve this?
Further, what if I'd want a QuerySet containing all the Content objects that are associated with Child1 objects?(eg. all the interviews that associated with musicians)
How does this change the filtering?
Thanks in advance
ps: I'm experiencing some problems with white space in the preview, forgive me
You should check the section of the Django docs regarding using related_name for abstract base classes. http://docs.djangoproject.com/en/dev/topics/db/models/#be-careful-with-related-name
To quote the docs:
If you are using the related_name
attribute on a ForeignKey or
ManyToManyField, you must always
specify a unique reverse name for the
field. This would normally cause a
problem in abstract base classes,
since the fields on this class are
included into each of the child
classes, with exactly the same values
for the attributes (including
related_name) each time.
To work around this problem, when you
are using related_name in an abstract
base class (only), part of the name
should be the string %(class)s. This
is replaced by the lower-cased name of
the child class that the field is used
in. Since each class has a different
name, each related name will end up
being different.
Using this information I would recommend moving the m2m field into the Base class:
class Content(models.Model):
# Add remaining fields for Content
pass
class Base(models.Model):
field1 = models.TextField()
items = models.ManyToManyField(Content,related_name="%(class)s_related")
class Meta:
abstract=True
class Child1(Base):
child1_field = models.TextField()
class Child2(Base):
child2_field = models.TextField()
Apparently a ForeignKey relation(or ManyToMany for that matter) with a abstract class isn't allowed.
I get the following error : 'AssertionError: ForeignKey cannot define a relation with abstract class Artiest'.
A possible solution is to define the base class as non-abstract, however this implies that one could instantiate models of the base class. Which isn't the behavior I want.(after all it was an abstract class)
Has someone come accross the same problem how did you solve it? Any alternatives?
Have a look at http://www.djangoproject.com/documentation/models/generic_relations/ which goes through generic relations. Your Content model would match up to their TaggedItem model, and your Base model would match up to their Animal/Vegetable/Mineral model (with Child1 and Child2 extending).
Getting all of the Content objects for a single child would be (assuming you set the GenericRelation to contents inside Base):
child_contents = childObject.contents.all()
And to get all Content objects for a model:
ctype = ContentType.objects.get_for_model(Child1)
all_child_contents = Content.objects.filter(content_type=ctype)