I'm essentially trying to come up with my own inheritance scheme because Django's inheritance doesn't fit my needs.
I'd like parent table(class) hold common data fields.
sub classess would have its own additional data in a separate table.
class ProductBase(models.Model):
common = models.IntegerField()
def get_price(self):
return some_price
class FooProduct(ProductBase):
# no field because I'm proxy
class Meta:
proxy = True
def get_price(self):
return price_using_different_logic
class FooExtra(models.Model):
base = models.OneToOneField(ProductBase, primary_key=True)
phone = models.CharField(max_length=10)
My question is, would it be able to treat as if Foo has FooExtra's fields?
I'd like to do things like following..
foo = FooProduct.objects.create()
foo.phone = "3333" # as django does with its multiple inheritance
foo.save()
FooProduct.objects.filter(phone="3333")
I'd like to list Products of different kind(data)
I need to list them together, so abstract Base inheritance is out
from the list, I'd like to treat each model as polymorphic model, when iterating over ProductBase.objects.all(), product.get_price() will use appropriate classe's method. (without incurring join if don't have to)
When and only when I want, I retrieve the addtional table data (by something like .select_related('fooextra')
Django-polymorphic is close to what I want, but it is rather obscure what it does so I'm afraid to use it, and I think it fails #3.
If I understand well, you want inheritance and you want the fields that are specific to the child class to be on a separate table.
As far as I know, you don't need a proxy class to achieve that, you could just implement multi-table inheritance as specified in the manual at https://docs.djangoproject.com/en/1.9/topics/db/models/#multi-table-inheritance e.g.:
class Base(models.Model):
common = models.IntegerField()
class Foo(Base):
phone = models.CharField(max_length=10)
This, as explained at the link above, will automatically create a one-to-one relationship. And of course you can do foo.phone = "3333" (where foo is of type Foo) as in your example above. And the neat thing is that you can also access foo.common whereas in your example it would have been foo.base.common.
It doesn't seem like you want anything different to Django's standard inheritance.
class ProductBase(models.Model):
common1 = models.IntegerField()
common2 = models.IntegerField()
class FooProduct(ProductBase):
fooextra = models.IntegerField()
class BarProduct(ProductBase):
barextra = models.IntegerField()
If you create instances of each:
foo1 = FooProduct(common1=1, common2=1, fooextra=1)
foo2 = FooProduct(common1=1, common2=1, fooextra=2)
bar1 = BarProduct(common1=1, common2=1, barextra=1)
bar2 = BarProduct(common1=1, common2=1, barextra=2)
You can loop over all products:
for product in ProductBase.objects.all():
print product.common1, product.common2
From a ProductBase object that is actually a FooProduct, you can get the custom field with:
product.foo.fooextra
From a ProductBase object that is actually a BarProduct, you can get the custom field with:
product.bar.barextra
You can still do querying:
foo = FooProduct.objects.get(fooextra=1)
bar = BarProduct.objects.get(barextra=2)
And you can access the common fields directly on those objects:
foo.common1
bar.common2
You can use the InheritanceManager from django-model-utils if you need more control over querying etc - and this should address point 3, too: ProductBase.objects.filter(...).select_subclasses() would give you the FooProduct and BarProduct objects instead of ProductBase objects.
Related
I am trying to write a generic method that can take any Django Model and returns it in a dictionary form.
So for example, if my models are defined thus (very generic):
class A(models.Model):
somefieldA = models.TextField()
m2mfield = models.ManyToManyField(B, through='AandB')
def __unicode__(self):
return self.somefieldA
class B(models.Model):
somefieldB = models.TextField()
def __unicode__(self):
return self.somefieldB
class AandB(models.Model):
a = models.ForeignKey(A)
b = models.ForeignKey(B)
field1 = models.DecimalField()
field2 = models.TextField()
field3 = models.DateField()
Now, assume we have an instance of the object A a_obj.
I can get all the related B objects using:
# This loop is there because I am working with other fields as well.
def instance_to_dict(instance):
for field in instance._meta.get_fields():
if field.many_to_many:
m2m_mgr = getattr(instance, field.name)
for idx, assoc_obj in enumerate(m2m_mgr.all()):
assoc_obj_str = str(assoc_obj)
# How to obtain the related through field values?
# m2m_mgr.through.objects.get() would need prior knowlegde
# of field name so get(a=instance, b=assoc_obj) is not possible
# m2m_mgr.through.objects.all() fetches all the objects
# in the Many to Many manager.
And then call instance_to_dict(a_obj). This method could be called by passing other models' instances.
Ideally, I would like to create a dict of the obj and related "through" fields for any object. Is this possible to do?
In addition to the explicitly defined ManyToMany manager, there is also an implicit reverse relationship for the ForeignKey from AandB to A. So you can do something like this:
for field in instance._meta.get_fields(include_hidden=True):
if field.one_to_many: # reverse ForeignKey
m2m_through_mgr = getattr(instance, field.get_accessor_name()) # e.g. aandb_set
m2m_through_mgr.all() # all related instances from the through table
Another approach is to go through the through table fields looking at field.related_model to see which one points back to your original table.
This all gets quite messy, but there should be enough meta information to do what you want. One obstacle is that the API isn't fully documented. Specifically, relation fields are represented by instances of the ManyToOneRel class, which as of Django 2.1 remains undocumented for reasons hinted at in the source code. Hence my use of the undocumented get_accessor_name() method.
I'm designing a new Django app and due to several possibilities, I'm not sure which would be the best, thus I'd like opinions, and hopefully improve what I got so far.
This question comes close but not quite. This one touches the flat/nested subject which is helpful, while still not answering the question.
There are many others on the same subject, and yet none tell me what I want to know.
Background
The models have each unique properties with some shared attributes, and I need to reference them in another model, optimally with a single entry point rather than having a field for each possible model.
I want to be able to do complex Django ORM queries involving the Base class and filter by SubClass when needed. E.g Event.objects.all() to return all events. I'm aware of Django model utils Inheritance Manager and intend to use it if possible.
Also, I'll be using django admin to create and manage the objects, so an easy integration is a must. I want to be able to create a new SubEvent directly, without having first to create a Event instance.
Example
To illustrate, let's say I have the following models for app A.
class Event(models.Model):
commom_field = models.BooleanField()
class Meta:
abstract = True
class SubEventA(Event):
email = models.EmailField(unique=True)
class SubEventB(Event):
title = models.TextField()
class SubEventC(Event):
number = models.IntegerField(default=10)
# and so on
And also an app B, where I want to be able to reference a event which can be of any type, like:
class OtherModel(models.Model):
event = models.ForeignKey('A.Event')
# This won't work, because `A.Event` is abstract.
Possible solutions
Use a GenericForeignKey.
# B.models.py
class OtherModel(models.Model):
content_type = models.ForeignKey(ContentType)
object_id = models.PositiveIntegerField()
event = GenericForeignKey('content_type', 'object_id')
What I don't like about this is that I'll lose the querying capabilities Django ORM has, and I might need to do additional fiddling to get it working on admin. Not sure, never dealt with this before
Flatten Event
I can bring it all up to the base class and have flags or checks outside the model definition, something like:
class Event(models.Model):
commom_field = models.BooleanField()
email = models.EmailField(blank=True)
title = models.TextField(blank=True)
number = models.IntegerField(default=10)
This might seem like the best idea at first, but of course there are other kind of fields, and that forces me to allow nulls/blanks for most of them (like the email field), losing the db level integrity check.
OneToOne relationships
Rather than abstract like on 1 or flatten on 2 it is possible to have a db table for each, where the models will look like:
class Event(models.Model):
commom_field = models.BooleanField()
class SubEventA(models.Model):
event = models.OneToOneField(Event)
email = models.EmailField(unique=True)
class SubEventB(models.Model):
event = models.OneToOneField(Event)
title = models.TextField(blank=True)
class SubEventC(models.Model):
event = models.OneToOneField(Event)
number = models.IntegerField(default=10)
So far it solved the two initial problems, but now when I get to the admin interface, I'll have to customize each form to create the base Event before saving a SubEvent instance.
Questions
Is there a better approach?
Can any of the choices I present be improved in any direction (ORM query, DB constraints, admin interface)?
I've pondered about both answers and came up with something based off of those suggestions. Thus I'm adding this answer of my own.
I've chosen to use django-polymorphic, quite nice tool suggested by #professorDante. Since this is a multi-table inheritance, #albar's answer is also somewhat correct.
tl;dr
django-polymorphic attends the 3 main requirements:
Allow django ORM querying style
Keep db level constraints by having a multi-table inheritance and one table for each sub class
Easy django admin integration
Longer version
Django-polymorphic allows me to query all different event instances from the base class, like:
# assuming the objects where previously created
>>> Event.objects.all()
[<SubEventA object>, <SubEventB object>, <SubEventC object>]
It also has great django admin integration, allowing seamless objects creation and editing.
The models using django-polymorphic would look like:
# A.models.py
from polymorphic import PolymorphicModel
class Event(PolymorphicModel):
commom_field = models.BooleanField()
# no longer abstract
class SubEventA(Event):
email = models.EmailField(unique=True)
class SubEventB(Event):
title = models.TextField()
class SubEventC(Event):
number = models.IntegerField(default=10)
# B.models.py
# it doesnt have to be polymorphic to reference polymorphic models
class OtherModel(models.Model):
event = models.ForeignKey('A.Event')
Besides, I can reference only the base model from another class and I can assign any of the subclasses directly, such as:
>>> sub_event_b = SubEventB.objects.create(title='what a lovely day')
>>> other_model = OtherModel()
>>> other_model.event = sub_event_b
My .2c on this. Not sure about your design in #3. Each SubEvent subclasses Event, and has a one-to-one to Event? Isn't that the same thing?
Your proposal on the Generic Key is exactly what it is designed for.
Another possibility - Polymorphism with Mixins. Use something like Django-polymorphic, so querying returns you the subclass you want. I use this all the time and its super useful. Then make Mixins for attributes that will be reused across many classes. So a simple example, making an email Mixin
class EmailMixin(models.Model):
email = models.EmailField(unique=True)
class Meta:
abstract = True
Then use it
class MySubEvent(EmailMixin, models.Model):
<do stuff>
This way you dont have redundant attributes on subclasses, as you would if they were all in the parent.
Why not a multi-table inheritance?
class Event(models.Model):
commom_field = models.BooleanField()
class SubEventA(Event):
email = models.EmailField(unique=True)
class SubEventB(Event):
title = models.TextField(blank=True)
class SubEventC(Event):
number = models.IntegerField(default=10)
So I have follwoing models:
class A(models.Model):
name = models.CharField()
age = models.SmallIntergerField()
class B(models.Model):
a = models.OneToOneField(A)
salary = model.IntergerField()
Now I want to create one rest end point for there two as they are one to one. So I want following as get
{
url: 'http://localhost/customs/1/',
name: 'abc',
age: 24,
salary: 10000
}
Similary, I want to create records and update as well. Please let me know how can I achieve this in django rest framework 3.
I just encountered the same problem, it would indeed be useful to make the response structure less tied to the underlying model structure. Here's my take :
Reading is easy
Serializer fields have a source parameter, which can take dotted names to traverse attributes.
class ABSerializer(serializers.ModelSerializer):
class Meta:
model = A
fields = ['name', 'age', 'salary']
salary = serializer.IntegerField(source='b.salary') # this is your related_name
Writing is ... not officially supported
Validated data will show a nested structure, and the standard create and update methods will choke trying to assign a data dict to a OneToOneField.
The good news is that you can work around it by overriding create and update methods. Here's an example with update :
class ABSerializer(serializers.ModelSerializer):
class Meta:
model = A
fields = ['name', 'age', 'salary']
related_fields = ['b']
salary = serializer.IntegerField(source='b.salary') # this is your related_name
def update(self, instance, validated_data):
# Handle related objects
for related_obj_name in self.Meta.related_fields:
# Validated data will show the nested structure
data = validated_data.pop(related_obj_name)
related_instance = getattr(instance, related_obj_name)
# Same as default update implementation
for attr_name, value in data.items():
setattr(related_instance, attr_name, value)
related_instance.save()
return super(ABSerializer,self).update(instance, validated_data)
Of course, this example is very simplistic, doesn't do any exception handling, and won't work with more deeply nested objects... but you get the idea.
Another option
You could also create a read-write flavor of SerializerMethodField, which would consider both a getter and a setter, however that would probably end up being far more verbose in the end.
Hope that helps !
I know this is an old post but I came across this and after some research and reading through the Django Rest Framework documentation
So a quick search I found that you could use the related_name parameter for reverse relationships as stated here:
reverse relationships are not automatically included by the
ModelSerializer and HyperlinkedModelSerializer classes. To include
a reverse relationship, you must explicitly add it to the fields list.
For example:
class AlbumSerializer(serializers.ModelSerializer):
class Meta:
fields = ['tracks', ...]
You'll normally want to ensure that you've set an appropriate
related_name argument on the relationship, that you can use as the
field name.
For example:
class Track(models.Model):
album = models.ForeignKey(Album, related_name='tracks',
on_delete=models.CASCADE)
...
If you have not set a related name for the reverse relationship,
you'll need to use the automatically generated related name in the
fields argument.
For example:
class AlbumSerializer(serializers.ModelSerializer):
class Meta:
fields = ['track_set', ...]
Also, see the Django documentation on reverse
relationships
for more details.
I'm working in a django project which I need to list two different models in the same view ordered by date. In order to achieve that I used inheritance to be able to get them all into a generic queryset. My models are:
class Publication(models.model):
title = models.CharField(max_lengh = 200)
pub_date = models.DateTimeField(default = datetime.now)
headline = models.TextField()
class Meta:
abstract = True
#abc.abstractmethod
def say_hello(self):
return
class New(Publication):
author = models.ForeignKey(Author)
source = models.CharField(max_length = 200)
categories = models.ManyToManyField(Category)
url = '/news/'
def say_hello(self):
return "Hello New!!!"
class Opinion(Publication):
writer = models.ForeignKey(Writer)
style = .models.CharField(max_length=3, choices=(('txt', 'Text'), ('glr', 'Galery')))
url = '/opinions/'
def say_hello(self):
return "Hello Opinion!!!"
I'm trying to call the subclass method while iterating through the Publication QuerySet like this:
publications = Publications.objects.all().order_by('-pub_date')
for pub in publications:
pub.say_hello()
url = pub.url
The problem is that my QuerySet is returning Publication objects, so I can't access child attributes and methods, obviously cus I'm dealing with Publication objects. Shouldn't The fact that I've set Publication as an abstract class, avoid the possibility of dealing with Publication objects?. Shouldn't they be prevented from being instantiated? Is there any option for perform perform a QuerySet in Publication class and return a list with child objects?
If no. How would you guys go around this situation? I could really use some tips.
Sounds like it might be appropriate to use multi-table inheritance and django polymorphic:
Multi-table inheritance: https://docs.djangoproject.com/en/dev/topics/db/models/#multi-table-inheritance
Django polymorphic: http://django-polymorphic.readthedocs.org/en/latest/
Multi-table inheritance in django allows you to have a base model/table which has your base fields. Your subclasses then define the extended fields which are put in their own tables. When you fetch records with querysets from any of the subclasses, you'll get information for each record from both the base model/table and the subclass model/table.
In order to fetch records using the base model's queryset, and get an instance of the appropriate subclass for each result, one option is django polymorphic. I've used it before and it works pretty well. It definitely has its limitations but I'd give it a shot.
Each Publication instance should have either a 'new' attribute or a 'opinion' attribute pointing to one of the two subclasses respectively. Be aware that each instance has only one of this attributes so maybe it's better to try...except access to them.
Well, I will put the code for my solution here which I achieved thanks to #David answer.
As suggested for David, I used django-polymorphic, which is great and simple. But the fact that I already had a populated database, made things a bit complicated. Nothing hard to fix.
First thing I did was to migrate the database with south in order to add the new field (polymorphic_ctype) to my parent model (no field is added to the subclasses).
Then, I used the following code in django shell mode on terminal. (python manage.py shell)
from jornal.models import Publication, New, Opinion
from django.contrib.contenttypes.models import ContentType
ctype_opinion = ContentType.objects.get(model = 'opinion', app_label = 'jornal')
ctype_new = ContentType.objects.get(model = 'new', app_label = 'jornal')
opinions = Opinion.objects.non_polymorphic().all()
news = New.objects.non_polymorphic().all()
for new in news:
new.polymorphic_ctype = ctype_new
new.save()
for opinion in opinions:
opinion.polymorphic_ctype = ctype_opinion
opinion.save()
Let's say that I have two teams, "red" and "black". And let's say that I have a Story class, which presents similar information in two very different ways, depending on your team:
class Story(models.Model):
red_title = models.CharField()
black_title = models.CharField()
red_prologue = models.TextField()
black_prologue = models.TextField()
# ... and so on ...
def get_field(self, genericName, team):
"""Return the field with suffix genericName belonging to the given team.
>>>self.get_field("prologue", "red") is self.red_prologue
True
>>>self.get_field("title", "black") is self.black_title
True
"""
assert(team in ["red", "black"])
specificName = "{}_{}".format(team, genericName)
return self.__dict__[specificName]
I'm happy with the getter function, but I feel like I should be able to refactor the code which created the fields in the first place. I'd like a function that looks something like this:
def make_fields(self, genericName, fieldType, **kwargs):
"""Create two fields with suffix genericName.
One will be 'red_{genericName}' and one will be 'black_{genericName}'.
"""
for team in ["red", "black"]:
specificName = "{}_{}".format(team, genericName)
self.__dict__[specificName] = fieldType(**kwargs)
But self and __dict__ are meaningless while the class is first defined, and I think Django requires that database fields be class variables rather than instance variables.
So... is there some way to create this make_fields function within Django, or am I out of luck?
Not sure why you're even doing this. A much more sane model would be:
TEAMS = (
("r","red"),
("b","black"),
)
class Story(models.Model):
team = models.CharField(max_length=1, choices=TEAMS)
title = models.CharField()
prologue = models.TextField()
Your current model is creating lots of duplicate columns (for red and black) that should just be defined by a column itself. Using the model above, you queries would be like Story.objects.filter(team="r").
You then wouldn't need your get_field function at all.
No. A Django model shouldn't be treated as something that can be dyamically constructed; it's a Python representation of a database table. For instance, what would be the semantics of changing the format of specificName after you had already run syncdb? There's no definitive, obvious answer - so Django doesn't try to answer it. You columns are defined at the class level, and that's that.
(At some level, you can always drill into the internal ORM data structures and set up these fields - but all you're doing is opening yourself up to a world of ambiguity and not-well-defined problems. Don't do it.)