ManyToMany relationship between inherited model and its parent - django

Probably easiest to explain with an example:
class Item(models.Model):
# ...
class ComplexItem(Item):
components = models.ManyToManyField(Item, through='ComponentItem', symmetrical=False, related_name='component_of')
class ComponentItem(models.Model):
# ...
item = models.ForeignKey(ComplexItem)
component = models.ForeignKey(Item, related_name='used_in_items
I would like a table of Items, with a name, price etc. Then I would like to define ComplexItems which are Items in their own right, but they require other Items in varying quantities.
The above causes the following exception in the admin app:
<class 'inventory.models.ComponentItem'> has more than 1 ForeignKey to <class 'inventory.models.ComplexItem'>
I need to override instance methods in ComplexItem and generally seperate the behavior from Item and the inheritance makes sense from a pure data view.
Is there some alternative definition of this relationship? I'd also like to avoid needing 'related_name' on both ComponentItem.component and ComplexItem.components.

You need to go back to the drawing board. While it's probably technically possible for a model to both inherit from and simultaneously be composed of another model, it's going to get sticky quick.
Try making ComplexItem just inherit from models.Model like Item does. Bet you that change alone will fix everything.

The model above actually works fine (I think, I haven't tested and decided against it for the moment). However the table generated for ComplexItem only has one column pointing to Item, which is fairly useless.
The functionality of ComponentItem can still be gotten by defining a ManyToMany relationship from Item to 'self' through ComponentItem.
Defining separate behavior is as easy as creating a Proxy model.
The actual error above came from my admin.Inline not being able to pick the correct foreign key to use for a ComponentItem, which can be solved like this.
I may come back to the inheritance above, but this works for now.

Related

Django: conditional ModelAdmin depending on object

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.

Creating Dynamic Forms in Django

I'm working on a project that involves a form with some standard fields and some custom field users define later. The standard forms are defined on a model in models.py. For example:
class Order(models.model):
number = models.TextField()
date = models.DateField()
I then use this model to create a simple model form to make a way to fill in the information. That's pretty standard Django.
The tricky thing is that my users want to be able to add arbitrary fields to the form. They would like to be able to use the Admin interface to basically modify the form and add values to it at run time.
So, they might want a new text field called "Tracking Number" or something like that. The trick is that they only need it sometimes and they want to be able to add it dynamically without rebuilding the whole database.
I can create a fairly simple model to represent the custom fields like so:
class CustomField(models.Model):
type = models.CharField(choices=FIELD_TYPES)
required = models.BooleanField()
I think I can then take the ModelForm for the Order class and extend it to add these custom fields. What I am unsure of is how to link the custom field values back to the Order.
I know this all might sound odd, but in practice it makes sense. Each user has slightly different needs for the form and want to tweak it. If I have to hard code the models to have their specific fields, then I will have to have a fork for each user. That simply doesn't scale. If instead they can simply add the fields through the admin interface, then things are much simpler.
I feel like this is something that is perhaps already solved by someone out there. I simply cannot find a solution. I can't be the only one who has gotten this kind of request right?

Django: using ContentType vs multi_table_inheritance

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.

Smarter removing objects with many-to-many relationship in Django admin interface

I'd like to remove some object with many-to-many relationship using Django admin interface. Standard removing also removes all related objects and the list of removed objects displayed on confirmation page. But I don't need to remove related objects!
Assume we have ContentTopic and ContentItem:
class ContentTopic(models.Model):
name = models.CharField()
code = models.CharField()
class ContentItem(models.Model):
topic = models.ManyToManyField(ContentTopic, db_index=True,\
blank=True, related_name='content_item')
So, I'd like to remove ContentTopic instance using Django admin, but I don't need remove all related ContentItems. So, confirmation page should display only ContentTopic instance to remove.
What is the best way to handle this?
This happens so, coz its developed to do so.
If you want to change this behaviour, the one way can be over-riding delete method of django.db.models.Model.
This delete() method actually does two things, first gathering a list of all dependent objects and delete them. So here, you can override it, to get that list of dependent objects, iterating over it and set their reference to None, instead of deleting them. And thus deleting the concerned object cleanly.
May be if you want this behavior throughout, you can extend a class from django.db.models.Models, override delete(), and extend all your models from this new class.

How do I implement a common interface for Django related object sets?

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')