I have tried multiple aproaches, but as I cannot use self in the class body, self.__class__.__name__ is not available. Would I need to override the save method to do this? Thanks for your help.
Your question is oddly phrased, so I'm going to come at it sideways.
Assume that you have defined a model Foo as follows:
from django.db import models
class Foo( models.Model ):
foo = models.IntegerField()
bar = models.IntegerField()
def klass( self ):
return self.__class__.__name__
Supposing you start a Django shell (python manage.py shell), you can do the following:
>>> from foo.models import Foo
>>> foo = Foo()
>>> print foo.klass()
Foo
This demonstrates that you can certainly use self.__class__.__name__ in the body of any method for model Foo. Thus you must have some other context where you need to dynamically determine the actual class name for your model, but not from an instance of your model.
If you've finished defining the model, then the following is legal:
>>> print Foo._meta.object_name
Foo
This mechanism would allow you to do introspection directly against the model, without having to create an instance of the model.
If this doesn't work for you, you must need this during the actual definition of the model. In that case, I would respectfully suggest that if you know you're defining the Foo model, you should just hardcode Foo wherever you need it. If you really need a dynamic way during the creation of your model to determine the name of the model ... could you describe the actual problem you're trying to solve, so that we can help you solve it?
This is more or less what I want:
class VFXContainer(models.Model):
classname=models.CharField(max_length=60,editable=False,blank=True)
parent=models.ForeignKey("self",blank=True,null=True)
def save(self, *args, **kwargs):
self.classname=self.__class__.__name__
super(VFXContainer, self).save(*args, **kwargs)
class Company(VFXContainer):
class Meta:
verbose_name_plural="companies"
class Project(VFXContainer):
pass
class CustomVFXContainer(VFXContainer):
pass
Now, what I dont know how to do, is I want to "override" the limit_choices_to option in the parent field on the child classes. What I want is CustomVFXContainer to be parented to any type of class, Project only to be parented by Company, and Company not to be parented at all. Im using this structure for the following reason. There is going to be a ton of fields that I want to be in all the subclasses, and I also have a separate Tasks models that link through a foreign key to the base VFXContainer Class (and thus is attachable to any of the child classes). Hope this makes it more clear on what Im trying to achieve, thanks for your help.
Related
I have a bunch of classes that I'm now trying to incorporate into django.
For example, I have a Base class that all my other classes derive from:
class Base:
def __init__(self, label: str = 'Base'):
self.label = label
An example of a sublcass would be a Person class:
from typing import Any, Dict
class Person(Base):
def __init__(self, name: str, attributes_to_options: Dict[str, Any], **kwargs):
super().__init__(**kwargs)
self.name = name
self.attributes_to_options = attributes_to_options
I would use this as:
alex = Person(name='Alex', attributes_to_options={'age': 10, 'is_happy': True}, label='Person:Alex')
My question is, how do I incorporate such a class into django? Is it as simple as inheritting from models.Model? e.g.
from django.db import models
class Person(Base, models.Model):
def __init__(self, name: str, attributes_to_options: Dict[str, Any], **kwargs):
super().__init__(**kwargs)
self.name = name
self.attributes_to_options = attributes_to_options
But then how do I specify the models.CharField for the two attributes name and attributes_to_options?
Thanks for any help here.
Bear in mind that in general, any Django Model subclass corresponds to a database table. Inheriting from such a class ("concrete inheritance") means that another database table will be created with a one-to-one linkage between rows, and that every query will implicitly perform a join in the DB. This is bad for performance. But for tables with not very many rows or for tables queried infrequently, you possibly don't need to care.
Django provides two special cases that can be defined via the Meta class in the Model subclass. The first is an "Abstract Base Class" which allow you to define a bunch of stuff which will be present in any derived model. IN the case of fields, they are "copied" into the class that inherits them, rather than having their own DB table. The second is a "Proxy" class, which allows you to place a new set of methods on top of an existing database table, and which goes some way towards allowing polymorphic models. Careful reading of the Django doc. is a good idea.
I have experimentally established that one can also use mix-in classes in the same way as one uses them with class-based views. In other words,
class ExtraMethodsMixin( object): # NB must inherit from object
# NB no model field definitions allowed here
#property
def something_or_other(self):
return something_based_on_model_fields_defined_elsewhere
#etc.
and then
class Foo( ExtraMethodsMixin, models.Model): # NB mixin goes first
# define names and fields that the ExtraMethodsMixin uses
# (and anything else that a Foo needs)
...
The one snag I have found is that migrations do remember the dependency of Foo on ExtraMethodsMixin and so it's a PITA should you desire to completely remove the mixin at a later date. However, you can stub it out to a single pass statement without any problems, so this is probably not a significant worry. My other worry is that this usage is completely undocumented (other than as standard Python), so it's just about possible that it's trampling on Django internals in some very subtle way I've not yet spotted. So I'm definitely a bit short of recommending this technique.
The question title is quite the sentence, but hopefully the code below will clear it up:
class Foo(models.Model):
...
class AbstractParent(models.Model):
foos = models.ManyToManyField(
Foo,
related_name='%(app_label)s_%(class)s_related'
)
def bar(self):
...
class Meta:
abstract = True
class ChildOne(AbstractParent):
...
class ChildTwo(AbstractParent):
...
Lets say that my app's label is 'myapp'.
Basically, the base class of the ChildOne and ChildTwo has a M2M to the class Foo. What I want to do is this: whenever an object of the Foo class is saved, I want to call the bar() method of all the objects of ClassOne and ClassTwo which has a relation to the Foo object through the foos field. To do this, I tried writing a simple signal:
from django.db.models.signals import post_save
from django.dispatch import receiver
#receiver(post_save, sender=Foo)
def call_bar_for_all_related_objects_to_foo(sender, instance, **kwargs):
# Do the thing
At this point, Im kinda lost. How do I query all the children classes of the AbstractParent class and call their bar() methods whenever there is a relation to the instance in the signal? Ideally, I want to query my database only once, and in one query, I want to get all the objects of ChildOne and ChildTwo related to the instance in the signal. Please note, in my actual models, there are more than two child classes of the AbstractParent, so an answer that keeps that in mind is appreciated. Thanks for any help.
Well, it doesn't satisfy your single query requirement, but here's a way to at least get the job done with a query per child class:
def call_bar_for_all_related_objects_to_foo(sender, instance, **kwargs):
for field in instance._meta.get_fields():
if not field.related_model:
continue
if not issubclass(field.related_model, AbstractParent):
continue
for related_object in getattr(instance, field.related_name).all():
related_object.bar()
Single query update
I don't think this can be done in a single query in a general way like this. The only way I know to get whole related django objects out of a single query is via select_related, which doesn't work with ManyToMany relationships.
If a single query is important, implementation will probably require more specific details on .bar(), which would likely need to be refactored into a class method that works on the results of a .values() call or something.
The main purpose of a model is to contain business logic, so I want most of my code inside Django model in the form of methods. For example I want to write a method named get_tasks_by_user() inside task model. So that I can access it as
Tasks.get_tasks_by_user(user_id)
Following is my model code:
class Tasks(models.Model):
slug=models.URLField()
user=models.ForeignKey(User)
title=models.CharField(max_length=100)
objects=SearchManager()
def __unicode__(self):
return self.title
days_passed = property(getDaysPassed)
def get_tasks_by_user(self,userid):
return self.filters(user_id=userid)
But this doesn't seems to work, I have used it in view as:
tasks = Tasks.objects.get_tasks_by_user(user_id)
But it gives following error:
'SearchManager' object has no attribute 'get_tasks_by_user'
If I remove objects=SearchManager, then just name of manager in error will change so I think that is not issue. Seems like I am doing some very basic level mistake, how can I do what I am trying to do? I know I can do same thing via :Tasks.objects.filters(user_id=userid) but I want to keep all such logic in model. What is the correct way to do so?
An easy way to do this is by using classmethod decorator to make it a class method. Inside class Tasks:
#classmethod
def get_tasks_by_user(cls, userid):
return cls.objects.filters(user_id=userid)
This way you can simply call:
tasks = Tasks.get_tasks_by_user(user_id)
Alternatively, you can use managers per Tom's answer.
To decided on which one to choose in your specific case, you can refer James Bennett's (the release manager of Django) blog post on when to use managers/classmethod.
Any methods on a model class will only be available to instances of that model, i.e. individual objects.
For your get_tasks_by_user function to be available as you want it (on the collection), it needs to be implemented on the model manager.
class TaskManager(models.Manager):
def get_tasks_by_user(self, user_id):
return super(TaskManager, self).get_query_set().filter(user=user_id)
class Task(models.Model):
# ...
objects = TaskManager()
When using Model class like this:
class MyModel(models.Model):
def __init__(self, *args, **kwargs):
self.myfield = models.Field()
super(MyModel, self).__init__(*args, **kwargs)
It doesn't take into consideration myfield(in the admin form, when saving the object... )
But if i declare like that:
class MyModel(models.Model):
myfield = models.Field()
It works just fine.
Why?
Edit
I think i have a good reason: I have an abstract class UploadItem that defines a field called file like this: self.file = models.FileField(upload_to=upload_to) As you can see, in each child class, i have to call parent init method with appropriate upload_to variable(say 'videos' for Video model). So i cannot do it the normal way.
Because the Django ORM code does some serious meta-magic during class definition (just browse the django/db code to see how magic). You are doing an end-run around that magic by creating fields on the fly in the __init__() function.
Is there a really good reason for not creating the class in the normal way? If not, then do it the normal way. If you do have a good reason then get ready to get into the really deep end of the pool -- both of Python and Django.
Setting a dynamic path for the upload_to attribute is absolutely not a good reason for wanting to muck around with model field declaration.
This is something that Django handles already - if you set upload_to to a callable, you can return the correct value dependent on the model instance. See the documentation.
I have a design question concerning Django. I am not quite sure how to apply the principle of loose coupling of apps to this specific problem:
I have an order-app that manages orders (in an online shop). Within this order-app I have two classes:
class Order(models.Model):
# some fields
def order_payment_complete(self):
# do something when payment complete, ie. ship products
pass
class Payment(models.Model):
order = models.ForeignKey(Order)
# some more fields
def save(self):
# determine if payment has been updated to status 'PAID'
if is_paid:
self.order.order_payment_complete()
super(Payment, self).save()
Now the actual problem: I have a more specialized app that kind of extends this order. So it adds some more fields to it, etc. Example:
class SpecializedOrder(Order):
# some more fields
def order_payment_complete(self):
# here we do some specific stuff
pass
Now of course the intended behaviour would be as follows: I create a SpecializedOrder, the payment for this order is placed and the order_payment_complete() method of the SpecializedOrder is called. However, since Payment is linked to Order, not SpecializedOrder, the order_payment_complete() method of the base Order is called.
I don't really know the best way to implement such a design. Maybe I am completely off - but I wanted to build this order-app so that I can use it for multiple purposes and wanted to keep it as generic as possible.
It would be great if someone could help me out here!
Thanks,
Nino
I think what you're looking for is the GenericForeignKey from the ContentTypes framework, which is shipped with Django in the contrib package. It handles recording the type and id of the subclass instance, and provides a seamless way to access the subclasses as a foreign key property on the model.
In your case, it would look something like this:
from django.db import models
from django.contrib.contenttypes.models import ContentType
from django.contrib.contenttypes import generic
class Payment(models.Model):
order_content_type = models.ForeignKey(ContentType)
order_object_id = models.PositiveIntegerField()
order = generic.GenericForeignKey('order_content_type', 'order_object_id')
You don't need to do anything special in order to use this foreign key... the generics handle setting and saving the order_content_type and order_object_id fields transparently:
s = SpecializedOrder()
p = Payment()
p.order = s
p.save()
Now, when your Payment save method runs:
if is_paid:
self.order.order_payment_complete() # self.order will be SpecializedOrder
The thing you want is called dynamic polymorphism and Django is really bad at it. (I can feel your pain)
The simplest solution I've seen so far is something like this:
1) Create a base class for all your models that need this kind of feature. Something like this: (code blatantly stolen from here)
class RelatedBase(models.Model):
childclassname = models.CharField(max_length=20, editable=False)
def save(self, *args, **kwargs):
if not self.childclassname:
self.childclassname = self.__class__.__name__.lower()
super(RelatedBase, self).save(*args, **kwargs)
#property
def rel_obj(self):
return getattr(self, self.childclassname)
class Meta:
abstract = True
2) Inherit your order from this class.
3) Whenever you need an Order object, use its rel_obj attribute, which will return you the underlying object.
This solution is far from being elegant, but I've yet to find a better one...