I have a custom save method for a model.
class Ticket(models.Model):
show = models.ForeignKey(Show)
seat = models.ForeignKey(Seat)
ref = models.CharField(max_length=100)
paid = models.BooleanField(default=False)
class Meta:
unique_together = ('show', 'seat')
def save(self, *args, **kwargs):
if self.paid:
do_something()
In the view I would like to update multiple Ticket objects:
Ticket.objects.filter(ref='ref').update(paid=True)
But, since this won't call the custom save method. The method do_something() won't be processed. Is there any way to solve this problem?
The obvious solution would be:
for ticket in Ticket.objects.filter(ref='ref'):
ticket.paid = True
ticket.save()
If you are doing the update for performance reasons you don't want to give up, you could do:
new_paid_tickets = Ticket.objects.filter(ref='ref')
new_paid_tickets.update(paid=True)
for ticket in new_paid_tickets:
do_something()
def save(self, *args, **kwargs):
if self.paid:
do_something()
super(Ticket, self).save(*args, **kwargs)
use this and add the custom code inside self.paid, hope it helps
Quote from docs:
Be aware that the update() method is converted directly to an SQL statement. It is a bulk operation for direct updates. It doesn’t run any save() methods on your models, or emit the pre_save or post_save signals (which are a consequence of calling save()), or honor the auto_now field option. If you want to save every item in a QuerySet and make sure that the save() method is called on each instance, you don’t need any special function to handle that. Just loop over them and call save().
So you need to iterate over queryset and call save() method for each element.
Related
I have a model field with choices:
class MyModel(models.Model):
myfield = models.CharField(max_length=1000, choices=(('a','a'),('b','b'))
I know that I can access in forms this specific field and override its choices option like that:
self.instance._meta.get_field(field_name).choices = (('c','c'),('d','d'))
but that will change the choices for the entire model, not for an individual instance. What is the correct way to do it for one specific instance only or it is not possible?
I'm not aware of any way to change the model's field choices on a per-instance basis, but if it's for a form you can override the form's field choices (example written from memory so it might no be 100% accurate):
class MyModelForm(forms.ModelForm):
class Meta:
model = MyModel
def __init__(self, *args, **kw):
super(MyModelForm, self).__init__(*args, **kw)
if some_condition:
self.fields["myfield"].choices = (...)
Important: you want to override self.fields["myfield"], not self.myfield - the latter is a class attribute so changing it would affect all MyModelForm instances for the current process, something you won't usually notice when running the dev server but that will cause very erratic behaviour on production.
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()
I have a System model in one app that I'd like to connect to a Queue model in another app (django-helpdesk). If no Queue object is selected for a System object, I'd like to initialize a new Queue object, using information from the System to generate it.
I've covered all of the required fields for the Queue, and yet this doesn't seem to work. It doesn't throw an error; it just never generates a new Queue object. Can anyone spot the issue in question, or recommend another way of covering this?
#models.py
import helpdesk
....
class System(models.Model):
queue = models.ForeignKey(
helpdesk.models.Queue,
blank = True,
null = True,
editable = True,
verbose_name = _('Queue'),
)
def __save__(self, *args, **kwargs):
if not self.queue:
slug = slugify(self.name)
queue = helpdesk.models.Queue(title=self.name, slug=slug)
queue.save()
self.queue = queue.pk
super(System, self).save(*args, **kwargs)
EDIT: For reference, there's an additional mistake beyond using the double underscores for the save() method. The line self.queue = queue.pk should instead be self.queue = queue.
You should override save() method, not __save__(), see examples:
Django. Override save for model
How do you detect a new instance of the model in Django's model.save()
Rename:
def __save__(...)
to
def save(...)
No need for the double underscores in the save() method. That's for Python magic methods only.
My question, how to override model manager's method for passing default parameter, if it doesn't set explicitly?
Say i have a model Entry:
class Entry(models.Model):
user = models.ForeignKey(User, verbose_name=_("user"))
text = models.CharField(max_length=200)
date = models.DateTimeField(auto_now_add=True)
deleted = models.BooleanField(default=False)
Now somehow i want in all get() and filter() methods add parameter deleted=False if this parameter isn't set explicitly (i mean in stuff like Entry.objects.filter(deleted=True) default parameter shouldn't override presented one).
What i do is create EntryManager and set it as manager in Entry model.
filter() method in EntreManager:
def filter(self, *args, **kwargs):
deleted = kwargs.get('deleted', False)
kwargs.update({'deleted':deleted})
return super(EntryManager, self).get(*args, **kwargs)
But it doesn't work for case deleted__in=(True, False), and it's not strange but i don't know ways how to do this right.
Thanks in advance.
By the way i use django 1.3.1
You should override the manager's get_query_set method, as described here. In your case, you could do something like this:
def get_query_set(self):
return super(EntryManager, self).get_query_set().filter(deleted=False)
Hay, i have a simple model
class Manufacturer(models.Model):
name = models.CharField()
car_count = models.IntegerField()
class Car(models.Model):
maker = ForeignKey(Manufacturer)
I want to update the car_count field when a car is added to a manufacturer, I'm aware i could just count the Manufacturer.car_set() to get the value, but i want the value to be stored within that car_count field.
How would i do this?
EDIT
Would something like this work?
def save(self):
if self.id:
car_count = self.car_set.count()
self.save()
The best way make something happen when a model is saved it to use a signal. Django's documentation does a good job of describing what signals are and how to use them: http://docs.djangoproject.com/en/dev/topics/signals/
I'm not sure why you need to make it a field in the model though. Databases are very good at counting rows, so you could add a model method to count the cars which would use a very fast COUNT() query.
class Manufacturer(models.Model):
name = models.CharField()
def car_count(self):
return Car.objects.filter(maker=self).count()
class Car(models.Model):
maker = ForeignKey(Manufacturer)
In light of the requirement added by your comment, you're back to updating a field on the Manufacturer model whenever a Car is saved. I would still recommend using the count() method to ensure the car_count field is accurate. So your signal handler could look something like this:
def update_car_count(sender, **kwargs):
instance = kwargs['instance']
manufacturer = instance.maker
manufacturer.car_count = Car.objects.filter(maker=self).count()
manufacturer.save()
Then you would connect it to both the post_save and post_delete signals of the Car model.
post_save.connect(update_car_count, sender=Car)
post_delete.connect(update_car_count, sender=Car)
The proper way to let the database show how many cars a manufacturer has, is to let the database calculate it in the view using aggregations.
from django.db.models import Count
Manufacturer.objects.all().annotate(car_count=Count(car)).order_by('car_count')
Databases are very efficient at that sort of thing, and you can order by the result as seen above.
I'm a tiny bit confused.
.. when a car is added to a manufacturer ..
In the code shown in your question, I'd guess, you save a car with some manufacturer, e.g.
car.maker = Manufacturer.objects.get(name='BMW')
car.save()
Then the save method of the Car class would need to update the car_count of the manufacturer (see Overriding predefined model methods for more details).
def save(self, *args, **kwargs):
if self.id:
self.maker.car_count = len(self.maker.car_set.all())
super(Car, self).save(*args, **kwargs)
Since this isn't the most elegant code, I'd suggest as #Josh Wright to look into signals for that matter.
P.S. You could also add a method on the Manufacturer class, but I guess, you want this attribute to live in the database.
class Manufacturer(models.Model):
name = models.CharField()
def _car_count(self):
return len(self.car_set.all())
car_count = property(_car_count)
...
The override in MYYN's answer won't work, since Car.id won't be set (and probably not included in the Manufacturer's car_set) until it's saved. Instead, I'd do something like:
def save(self, *args, **kwargs):
super(Car, self).save(*args, **kwargs)
self.maker.car_count = len(self.maker.car_set.all())
self.maker.save()
Which is untested, but should work.
Of course, the best way is to use Josh's solution, since that's going 'with the grain' of Django.