I have the following (simplified) models:
class Structure(models.Model):
name=models.CharField(max_length=100, unique=True)
class Unit(models.Model):
name=models.CharField(max_length=100, unique=True)
Each model, also has a builtFrom field, which shows what the item is built from, for example:
class Unit(models.Model):
name=models.CharField(max_length=100, unique=True)
builtFrom=models.ForeignKey(Structure)
However, builtFrom can be populated from either a Unit type, or a Structure type. Is there an easy way to represent this in my models?
The only thing I can think of is to have a separate model, like so:
class BuiltFromItem(models.Model):
structure=models.ForeignKey(Structure)
unit=models.ForeignKey(Structure)
class Unit(models.Model):
name=models.CharField(max_length=100, unique=True)
builtFrom=models.ForeignKey(BuiltFromItem)
And then have one of the BuiltFromItem fields just be null. Then, when I need the data, figure out whether it is a structure or unit that it is built from. Is there a better solution for this?
You want what the Django docs refer to as a "generic relation". Support for them is built into Django.
Generic relation is probably the best approach, yet it can be a little problematic, if you're planning to manage such models via admin panel. You would then have to add a ModelInline to the models, that generic relation is pointing to, but as far as I know (correct me if I'm wrong), there's no convenient way of picking related object from the other side (from model, where relation is defined), other than choosing model class and manually typing instance primary key.
Picking the best solution actually depends on structure of your models and on what they have in common. Another idea I have, is to use Multi-table inheritance, by defining some BasicObject that is a parent object to Structure and Unit models:
class BasicObject(models.Model):
name=models.CharField(max_length=100, unique=True)
#other common data
builtFrom=models.ForeignKey('BasicObject')
class Structure(BasicObject):
#data specific to Structure
class Unit(BasicObject):
#data specific to Unit
Now all Structure and Unit object will also be BasicObject instances, and you will be able to populate builtFrom field with proper BasicObject instance. It makes queries more expensive, because data is divided into two diffrent tables, so you should consider if this approach is beneficial in your case.
Related
I need to capture some fairly complicated database changes from my users, including both updating and creating objects for multiple models.
I feel like the obvious way to do this would be by leveraging a sizeable amount of Javascript to create a JSON object containing all the necessary changes that can be POSTed in a single form. I am not keen on this approach as it prevents me from utilizing Django's CreateView and UpdateView classes, as well as the validation that comes with them. Also I am more comfortable in Python than Javascript.
I want to use a series of form POSTs to build up the necessary changes over time, but also need the transaction to be atomic, which, as far as I know, is not possible in Django. Another complication is that the models contain non-nullable fields and I would need to create objects before capturing the user input required to fill them. I do not want to make these fields nullable or use placeholders as this would make it more difficult to validate.
One approach I am considering is to create a duplicate of each of the necessary models to store partial objects. All fields would be nullable so the objects could be updated a bit at a time until all the forms have been POSTed. Objects in the original (main) model could then be created or updated to match the ones in the new (partial) model, which could then be deleted.
class Product(models.Model):
field_a = models.CharField(max_length=255)
field_b = models.PositiveIntegerField()
class PartialProduct(models.Model):
field_a = models.CharField(max_length=255, blank=True, null=True)
field_b = models.PositiveIntegerField(blank=True, null=True)
The benefits of this approach as I see are:
A multi-form approach, leveraging Django's model forms and related views as well as model validation.
Not polluting the main models with incomplete objects.
Enforcing fields not being null in the main models.
The potential drawbacks I can see are:
Duplicating any changes to the main model in the partial model (the approach is not DRY).
It is a somewhat complicated approach (Simple is better than complex)
Are there any drawbacks to using this approach that I have not foreseen, or is there a better one I could use?
I am using Multi-Table inheritance (aka Concrete Inheritance), where I have a non-abstract model + DB Table called Clients, which is concerned with common details concerning all the clients.
But a client can be an Individual, Partnership or Company, for which I have created inheriting models and tables. An Individual has first name + last name, and company has other specific particulars, etc.
I want to be able to access the names of clients (derived from the columns from the child tables) when I want a list of all clients.
After lot of searching, I found that this tutorial, works successfully.
Basically, it involves inserting a column on the Client table, which will store the name of the Child model. Then using that name, the appropriate child model is identified and appropriate child method is accessed.
But it seems to be a slightly cumbersome way to implement polymorphism in Multi-Table inheritance.
I want to know whether since 2012, Django has introduced any better way to deal with the issue, or is this still the only way?
Please let me know if my code sample is required, but the link provided has a beautiful example already.
There is django-model-utils application with Inheritance Manager. It will cast automatically your parent class to children instances. Example from docs:
from model_utils.managers import InheritanceManager
class Place(models.Model):
# ...
objects = InheritanceManager()
class Restaurant(Place):
# ...
class Bar(Place):
# ...
nearby_places = Place.objects.filter(location='here').select_subclasses()
for place in nearby_places:
# "place" will automatically be an instance of Place, Restaurant, or Bar
Also check this question for generic solution with ContentType.
And also check awesome article by Jeff Elmore about this topic. Quite old, but still great.
Let's say I have a base class with, for example:
class Base(models.Model):
name = models.CharField(max_length=50, blank=False, null=False)
value1 = models.CharField(max_length=50)
value2 = models.CharField(max_length=50)
Now, I'm inputting several types of objects into the table, some of which use parts of the data, some of which use other parts, all of them using some common part (name in this example).
I want a complete listing, but I want to have different views when I click into an object, depending on it's type. Changes in the modelAdmin include: one of the classes uses inlines, others don't, list_display varies, one has extra CSS, etc, etc. Basically we're talking about different modelAdmins.
Alternatives I'm thinking: one is that each of those types subclasses Base, i.e.:
class Type1(Base):
pass
class Type2(Base):
pass
and then I define a modelAdmin for each of them, and one for the Base class just to get the table listing everything. In this one I would override the links so they don't go to /app/base/id, but instead to /app/type1/id, /app/type2/id, etc depending on the type. For each of these, I modify the modelAdmins so after saving they go back to /app/type
A different alternative would be having a single model and a single modelAdmin, and overriding every single method I'm using for change_view to consider what type of object it's rendering, i.e., get_inline_instances, get_formsets, whatever I need to modify list_display, list_display_links, list_filter, etc.
The first alternative looks way cleaner to me, although I'm not sure how to modify the link other than defining a method in the modelAdmin with the correct call to reverse and adding that method as a column in list_display.
Is there an easier way I'm missing?. How would you do it?.
Oh, and it HAS to use the admin. I'd rather do this using views, or separate models, but sadly this is the way it has to be. The High Command wants everything in one single table.
Thanks!.
Edit: also, I just found this and it looks good:
http://django-polymorphic.readthedocs.org/en/latest/admin.html
Django-Polymorphic definitely seems the way to go. It's easy to use and automatically gives me the correct modelAdmin when I click through a base object, something I couldn't replicate with Proxies.
Only problem is a table is created for each child class, even if the child class doesn't have any additional fields, and an extra query is performed per child class even though nothing is recovered from it (only column in the table is a foreign key to the base object).
But it works. I can live with that.
I was having a similar problem as in
How to query abstract-class-based objects in Django?
The thread suggests using multi_table_inheritance. I personally think using content_type more conceptually comfortable (just feels more close to logic, at least to me)
Using the example in the previous link, I would just add a StelarType as
class StellarType(models.Model):
"""
Use ContentType so we have a single access to all types
"""
content_type = models.ForeignKey(ContentType)
object_id = models.PositiveIntegerField()
content_object = generic.GenericForeignKey('content_type', 'object_id')
Then add this to the abstract base model
class StellarObject(BaseModel):
title = models.CharField(max_length=255)
description = models.TextField()
slug = models.SlugField(blank=True, null=True)
stellartype = generic.GenericForeignKey(StellarType)
class Meta:
abstract = True
To sync between StellarObject and StellarType, we can connect post_save signal to create a StellarType instance every time a Planet or Star is created. In this way, I can query StellarObjects through StellarType.
So I'd like to know what's the PRO and CON of using this approach against using multi_table_inheritance? I think both create an additional table in the databse. But how about database performance? how about usability/flexibility? Thanks for any of your input!
To me, ContentType is the way to go when you want to relate an object to one of many models that aren't fundamentally of the same "type". Like if you want to be able to key Comments to Users, Pages, and Pictures on a social network, but there's no reasonable supertype shared by those three models. Sure you could create a "Commentable" supertype, but to me that feels more like a mixin than a fundamental type from which those three things derive. Before ContentType came out, you would have had no choice but to invent supertypes for these kind of relations, which can get really ugly really quickly if you need to do it multiple times in the same application (lets say you also have Events, Alerts, Messages, etc., each of which can apply to a different set of models).
Multi-table inheritance makes the most sense when you want to attach attributes to the base model, such that they will be shared in all concrete models that extend from it, so that you can get polymorphic behavior. Commentable doesn't really fit this mold, because all of that behavior can be put on the Comment model, less so on the Commentable objects. But if you have different classes of Users that share much of the same behavior and should be aggregable, then it makes a lot more sense.
The major pro of multi-table inheritance to me is a cleaner data model, with implicit relationships and inheritance that can be taken advantage of on the Python side (polymorphism is still a bit messy though, as seen here and here). The major pro of ContentType is that it is more general and keeps auxiliary functionality out of your models, at the cost of a bit of a slightly less pristine schema (lots of "meta" fields on your models to define these relationships). And for your example, you still have to rely on post_save, which seems unnecessarily messy/magical to me, as well.
Sorry for reviving old thread. I think it all boils down to the lookup direction. Whether you look up all subclasses for a certain FK (multitable inheritance) or define the referenced class as a content type and look it up based on the table reference and id (contenttypes) makes no big difference in performance - hint: they both suck. I think content types is a nice choice if you want your app to be easily extendible, i.e. others can add new content types to reference against. Multitable is good if you only sometimes need the extra columns defined in extra tables. Sometimes it might also be a good idea to merge all your subtypes and make only one which has a few fields left empty most of the time.
Here's the deal:
I got two db models, let's say ShoppingCart and Order. Following the DRY principle I'd like to extract some common props/methods into a shared interface ItemContainer.
Everything went fine till I came across the _flush() method which mainly performs a delete on a related object set.
class Order(models.Model, interface.ItemContainer):
# ...
def _flush(self):
# ...
self.orderitem_set.all().delete()
So the question is: how do I dynamically know wheter it is orderitem_set or shoppingcartitem_set?
First, here are two Django snippets that should be exactly what you're looking for:
Model inheritance with content type and inheritance-aware manager
ParentModel and ChildManager for Model Inheritance
Second, you might want to re-think your design and switch to the django.contrib content types framework which has a simple .model_class() method. (The first snippet posted above also uses the content type framework).
Third, you probably don't want to use multiple inheritance in your model class. It shouldn't be needed and I wouldn't be surprised if there were some obscure side affects. Just have interface.ItemContainer inherit from models.Model and then Order inherit from only interface.ItemContainer.
You can set the related_name argument of a ForeignKey, so if you want to make minimal changes to your design, you could just have ShoppingCartItem and OrderItem set the same related_name on their ForeignKeys to ShoppingCart and Order, respectively (something like "item_set"):
order = models.ForeignKey(Order, related_name='item_set')
and
cart = models.ForeignKey(ShoppingCart, related_name='item_set')