Django create instances from class attributes - django

Suppose such a Topic Model Table
class Topic(models.Model):
"""A topic the user is learning about."""
text = models.CharField(max_length=200)
date_added = models.DateTimeField(auto_now_add=True)
owner = models.ForeignKey(User)
def __str__(self):
"""Return a string representation of the model."""
return self.text
As the structure indicates, text, data_added, owner are class level attribute.
However, Django is capable of creating instance from the class attributes
In [21]: Topic.objects.create(text='Celery', owner_id=1)
Out[21]: <Topic: Celery>
In [34]: celery = Topic.objects.get(pk=22)
In [35]: isinstance(celery, Topic)
Out[35]: True
I assume there should be initiating process def __init__()
How Django accomplish such an amazing task?

__init__ is defined on the Model class here in the Django source.
Since you derive from Model, you're getting that __init__ from the superclass.
When you call QuerySet.get(), it eventually (through some hoops within QuerySet) calls Model.from_db() which calls the __init__ method (line 496).
Also, to elaborate: Even if text, date_added and owner look like class attributes, they end up not being present on the final model subclass, since the ModelBase metaclass gathers them up into the new model subclass's _meta class (which is an instance of Options).

Related

Best practice when updating Django model fields? Is this what manager classes are for?

I am very new to Django and I am wondering what the best practice is for updating a field. Here is my model:
class Website(models.Model):
id = models.AutoField(primary_key=True)
url = models.TextField()
is_awesome = models.BooleanField(default=False)
Right now I have a separate helper file, WebsiteHelper.py, with many other functions not related to the database in it, but also this function for updating a specific field in the DB:
def __mark_needs_redone(Website):
Website.update(is_awesome=True)
Is there a cleaner place for functions such as these to live, such as:
class WebsiteManager(models.Manager)
#Execute function here
Is this how managers are supposed to be used? If not, what is the best practice here?
If the field is on the model the form is handling, You can override the save() method you access to the actual instance.
class Website(models.Model):
id = models.AutoField(primary_key=True)
url = models.TextField()
is_awesome = models.BooleanField(default=False)
def save(self, commit=True):
self.instance.is_awesome = True
return super().save(commit)
Don't forget the super().save(commit) call after because the parent takes care of the saving logic
Your update() is calling the wrong way, you should call it to the model queryset, not the instance.
If You need to call Your method for every save(), check the pre_save signal, but if You don't, use Manager.
class WebsiteManager(models.Manager):
def mark_needs_redone(self, pk):
self.get(pk=pk).update(is_awesome = True)
To your model Website add the Manager:
class Website(models.Model):
id = models.AutoField(primary_key=True)
url = models.TextField()
is_awesome = models.BooleanField(default=False)
objects = WebsiteManager()
And the usage is:
Website.objects.mark_needs_redone(pk=1)
That code will mark is_awesome as True for Website with pk=1

Django Model Inheritance - get child

Is there a way to access the actual child of the base model, means: Staying with the example from the django Docs, let's assume I am modeling different delivery restaurants, that just have in common
name
all have a deliver method
as of this:
class Place(models.Model):
name = models.CharField(max_length=10)
class Pizzeria(Place):
topping = models.CharField(max_length=10)
tip = models.IntegerField()
def deliver(self):
deliver_with_topping(self.topping)
ask_for_tip(self.tip)
class Shoarma(Place):
sauce = models.CharField(max_length=10)
meat = models.CharField(max_lenght=10)
def deliver(self):
prepare_sauce_with_meat(self.sauce, self.meat)
I would now like to execute:
Place.objects.get(name="my_place").<GENERIC_CHILD>.deliver()
i.e. I don't need to know what the place is actually, just the common deliver method. The model then 'knows' what to call.
Is there something like <GENERIC_CHILD>?
I always use Inheritance Manager from django-model-utils for this kind of operations. On your models:
class Place(models.Model):
objects = InheritanceManager() #<- add inheritance manager
name = models.CharField(max_length=10)
def deliver(self):
pass #not needed
Your query:
Place.objects.get_subclass(name="my_place").deliver()
For me it is a clean and elegant solution. Don't forget to star-up django-model-util repo if you like it.
I did it in a messy way.
I do have parent class Activity, with childs - Action, Deal, Order classes.
I want to list them all in 1 place, 1) with a field specifieing it's class, 2) link them to same page, where i will render page based on Activity class
So in my model Activity i add:
def get_type(self):
children = ['action', 'deal', 'order']
for c in children:
try:
_ = self.__getattribute__(c) # returns child model
except ObjectDoesNotExist:
pass
else:
return c
else:
return 'Not specified'

Calling a model function in django-admin

is there any method I can call a model function in admin.py for eg suppose I have a model say
class A(models.Model):
first = models.IntegerField()
second = models.IntegerField()
total = models.IntegerField()
def Total_amount(self):
self.total+=first
self.total+=second
Now I want that whenever I do something from admin side into first and second it automatically must reflect into total's value.How can I do that I mean I can do something in ModelAdmin class but its not working from my side.
When you save a model in the admin, it calls the full_clean method on your model's instance. I suggest you override the clean() method on that model to get the behavior you want, e.g.:
def clean(self):
super(MyModel, self).clean()
self.Total_amount()

Django: Adding property to User model after creating model based on abstract class

I have a normal model and an abstract model like so:
class TaggedSubject(models.Model):
user = models.ForeignKey(User, null=True, blank=True)
category = models.CharField(max_length=200)
foo = models.CharField(max_length=50)
bar = models.CharField(max_length=50)
# etc
content_type = models.ForeignKey(ContentType)
content_object_pk = models.CharField(max_length=255)
content_object = generic.GenericForeignKey("content_type", "content_object_pk")
def __unicode__(self):
if self.user:
return "%s" % (self.user.get_full_name() or self.user.username)
else:
return self.label
class Taggable(models.Model):
tagged_subjects = generic.GenericRelation(TaggedSubject, content_type_field='content_type', object_id_field='content_object_pk')
#property
def tagged_users(self):
return User.objects.filter(pk__in=self.tagged_subjects.filter(user__isnull=False).values("user"))
class Meta:
abstract = True
The Taggable abstract model class then gets used like so:
class Photo(Taggable):
image = models.ImageField(upload_to="foo")
# ... etc
So if we have a photo object:
photo = Photo.objects.all()[0]
I can all the users tagged in the photo with photo.tagged_users.all()
I want to add the inverse relation to the user object, so that if I have a user:
user = User.objects.filter(pk__in=TaggedSubject.objects.exclude(user__isnull=True).values("user"))[0]
I can call something like user.tagged_photo_set.all() and have it return all the photo objects.
I suspect that since TaggedSubject connects to the Taggable model on a generic relation that it won't be possible to use it as a through model with a ManyToMany field.
Assuming this is true, this is the function I believe I'd need to add (somehow) to the User model:
def tagged_photo_set(self):
Photo.objects.filter(pk__in=TaggedSubject.objects.filter(user=self, content_type=ContentType.objects.get_for_model(Photo))
I'm wondering if it's possible to set it up so that each time a new model class is created based on Taggable, it creates a version of the function above and adds it (ideally as a function that behaves like a property!) to User.
Alternatively, if it is somehow possible to do ManyToMany field connections on a generic relation (which I highly doubt), that would work too.
Finally, if there is a third even cooler option that I am not seeing, I'm certainly open to it.
You could use add_to_class and the class_prepared signal to do some post processing when models subclassing your base class are set up:
def add_to_user(sender, **kwargs):
def tagged_FOO_set(self):
return sender.objects.filter(pk__in=TaggedSubject.objects.filter(
user=self,
content_type=ContentType.objects.get_for_model(sender)))
if issubclass(sender, MyAbstractClass):
method_name = 'tagged_{model}_set'.format(model=sender.__name__.lower())
User.add_to_class(method_name, property(tagged_FOO_set))
class_prepared.connect(add_to_user)

Django abstract models with M2M fields

Let's suppose I have the following:
class Base(Model):
m2m_1 = ManyToManyField("SomeModel1")
m2m_2 = ManyToManyField("SomeModel2")
class Meta:
abstract = True
class A(Base):
def __init__(self):
super(A, self).__init__()
pass
class B(Base):
def __init__(self):
super(B, self).__init__()
pass
However, I cannot do that because it requires related name for M2M field. However, that does not help as the model is abstract and django tries to create the same related name for both A and B models.
Any ideas how to specify related names for each model separately or even do not use them at all?
The answer is right in the docs for abstract classes (under section entitled "Be careful with related_name"):
m2m = models.ManyToManyField(OtherModel, related_name="%(app_label)s_%(class)s_related")