What I am trying to do is best described by the following example:
class MyAbstractClass(models.Model):
abstract_field = IntegerField()
class Meta:
abstract = True
def abstract_method(self):
# THE ISSUE LIES IN THE LINE BELOW
ParentClass.objects.filter(..).update(....)
return self
class InheritedClass(MyAbstractClass):
# Field
def my_view(request):
obj = InheritedClass.objects.get(id=1)
obj.save()
return obj
So basically, the question is, is there any way in the abstract_method to tell Django to address the calling class (that is, InheritedClass)?
Technical answer : Well, quite simply yes :
def abstract_method(self):
type(self).objects.filter(..).update(....)
return self
Note that this is Python methods are called with the "calling" object (the object on which the method is invoked) as first parameter, and all attributes lookups will happens on this object and it's class - else inheritance just wouldn't work at all. The only django-specific part here is that Django prevents you from using a ModelManager on a model instance so you need to explicitely get the object's class, which is returned by type(self).
BUT :
Coding style considerations
Django recommands that model methods acting on the whole table should belong to the ModelManager (by contrast with methods acting on the current row only which are to be implemented as plain methods), so since your method obviously acts on the whole table it might be better as a ModelManager method.
I say "might" because there's a grey area, where updating one row implies updating some other rows too - a typical example is when you have a flag that should always only be set for one single record so you also want to unset it on all other records. There's (of course) not enough context in your question to tell which is the right choice here.
you can just do self.objects.filter(...).update(..)
An abstract class just implements methods that are inherited by its concrete class, here InheritedClass. Therefore, everything for methods and fields are available in the inheriting class.
However, for this instance, I would suggest you look at making a custom model manager. Methods in a model are meant to work with that specific row's fields, whereas managers are intended to work table-wide as described at model methods
Define custom methods on a model to add custom “row-level” functionality to your objects. Whereas Manager methods are intended to do “table-wide” things, model methods should act on a particular model instance.
If you have a method doing filtering in a model method, that is a code smell and it belongs in a custom Manager.
Related
When defining models with foreign keys, Django asks the user to specify them as strings in order to avoid issues with circular dependencies.
What's the algorithm it uses to create the related models after parsing the strings? I've looked through Django's source code but haven't been able to understand it.
I thought it'd create a graph out of the models, topologically sort it and start by instantiating the models that don't depend on the rest. This, however, seems too simplistic since the graph may not be a DAG, as in the following example:
class ModelA:
b = ForeignKey(ModelB)
class ModelB:
c = ForeignKey(ModelC)
class ModelC:
a = ForeignKey(ModelA)
Thank you!
When you pass a string, Django first tries to look up the model. If it exists and it is registered, it is replaced immediately.
It the model isn't registered yet, a lazy operation is added to the app registry. For example, this method is used to solve the to part of a relation. The to attribute is replaced in-place with the actual model, and the reverse relation is added to the to model.
Whenever a new model class is defined, the metaclass registers the model in the app registry. The app registry the goes through the list of pending operations for that model, and fires each of them.
So, for each valid string reference, when the field is instantiated, the target either exists and the string is immediately replaced with the model class, or the target doesn't exist yet, but a lazy operation is used to replace the string with the model class when that model class is created and registered.
If you have a circular reference of, say, 2 models, the first model class will add a lazy operation. The second model class can immediately resolve its reference to the first model class, and then activates the lazy operation to resolve the reference from the first model class to the second model class.
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 see in the Django documentation :
Model Instance reference : Creating objects
You may be tempted to customize the model by overriding the __init__ method. If you do so, however, take care not to change the calling signature as any change may prevent the model instance from being saved.
Rather than overriding __init__, try using one of these approaches:
Add a classmethod on the model class.
Add a method on a custom manager (usually preferred)
Why is the second solution "usually preferred" ?
In a situation where I have a model B which extends a model A through a OneToOne relation, and I want to create a method generating a B object which generates the corresponding A object as well, how is it "better" to use a custom manager as suggested, given I'll probably not use this manager for anything other than what is provided by default manager ?
I think it is preferred because it looks cleaner in code. You might also be reading into the emphasizes a bit too much, as the benefit or difference isn't that big. That said, when implementing things myself I do use the proposed approach.
Consider the following model (purely for illustrative purposes):
class Vehicle(models.Model):
wheels = models.IntegerField()
color = models.CharField(max_length=100)
In your application, the need often arises to get all cars, or all motorcycles, or whatever type of vehicle. To keep things DRY, you want some standard form of retrieving this data. By using class methods, you'd get the following:
class Vehicle(models.Model):
#(...)
#classmethod
def cars(cls):
return Vehicle.objects.filter(wheels=4)
cars = Vehicle.cars()
green_cars = Vehicle.cars().filter(color='green')
If you create a manager, you'll get something like this:
class CarManager(models.Manager):
def get_query_set(self):
return super(CarManager, self).get_query_set().filter(wheels=4)
class Vehicle(models.Model):
#(...)
car_objects = CarManager()
cars = Vehicle.car_objects.all()
green_cars = Vehicle.car_objects.filter(color='green')
In my opinion, the latter looks cleaner, especially when things get more complex. It keeps the clutter out of your model definitions, and keeps things similar to using the default objects manager.
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.
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')