Extra parameter for Django models - python

With Django models, I want to achieve this:
class Foo(models.Model):
name = models.CharField(max_length=50)
#wrapping the save function, including extra tasks
def save(self, *args, **kwargs):
super(Foo, self).save(*args, **kwargs)
if extra_param:
...do task 1
else:
...do task 2
And while crating Foo I want to pass such as
Foo(name="Bill Gates",extra_param=True).save() # now triggers the task 1
Foo(name="Bill Gates").save() # now triggers the task 2
How can this be done? I am also open to any other suggestions :)
Thanks

You can define non-persistent fields in your model.
class Foo(models.Model):
name = models.CharField(max_length=50)
extra_param = False
def save(self, *args, **kwargs):
...
print self.extra_param
Alternatively, you can do:
Foo(name="Bill Gates").save(extra_param=True)
def save(self, *args, **kwargs):
...
print kwargs["extra_param"]

Related

Custom function in Django's model

I have model and i want to add my custom function, and when i create object this function call automatically.
this is my model and test function. it's only for testing
i want when i create Like object after call test function
class LikeManager(models.Manager):
def create(self, *args, **kwargs):
decrease = kwargs.pop("decrease")
new_like = self.model(**kwargs)
new_like.save(decrease=decrease)
return new_like
class Like(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE, verbose_name=_("user"))
question = models.ForeignKey(Question,on_delete=models.CASCADE,verbose_name=_("question"))
objects = LikeManager()
#this function (not printing)
#property
def test(self):
print("Testing")
return 1
def save(self, decrease, *args, **kwargs):
if not self.pk:
if decrease:
self.question.save()
else:
self.question.point += 1
self.question.save()
return super(Like, self).save(*args, **kwargs)
who can help me?
If you want to run the test function when a like object is created why don't you just put the test function inside of the save function?
class Like(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE, verbose_name=_("user"))
question = models.ForeignKey(Question,on_delete=models.CASCADE,verbose_name=_("question"))
objects = LikeManager()
def save(self, decrease, *args, **kwargs):
def test(self):
print("Testing")
return 1
if not self.pk:
if decrease:
self.question.save()
else:
self.question.point += 1
self.question.save()
return super(Like, self).save(*args, **kwargs)
Otherwise, you need to call the test function every time after you
create an object ( e.g.:
a = Like.objects.create(user=request.user,question=q)
a.test()
)

Form Problems - Setting Initial Value

I am trying to set the initial value of a field on a form. The field is not part of the model, but when I try and set it to a value the field is blank. From my research it could be because the form is "bound" which makes some sense to me, but in this case the field is not part of the model.
My form:
#Form for editing profile
class CatForm(forms.ModelForm):
pictureid = forms.CharField()
class Meta:
model = Cat
fields = ['name']
def __init__(self, *args, **kwargs):
picid = kwargs.pop("pictureid")
print(picid)
super(CatForm, self).__init__(*args, **kwargs)
self.fields['pictureid'] = forms.CharField(initial=picid, required=False)
The model:
class Cat(models.Model):
name = models.CharField(max_length=34,null=False)
From the view it is called like this:
catform = CatForm(request.POST, pictureid=instance.id)
I was expecting it to set the field to the value of the initial attribute, but it doesn't. I have tried testing it by directly adding a string, but doesn't set.
This is what seems to be working for me:
class CatForm(forms.ModelForm):
class Meta:
model = Cat
fields = ['name']
def __init__(self, *args, **kwargs):
picid = kwargs.pop("pictureid")
super(CatForm, self).__init__(*args, **kwargs)
self.fields['pictureid'] = forms.CharField(initial=picid)
I also needed to drop the "request.POST" from the call to this when initiating the form.
If you want to render the pictureid in GET request, then you can try like this:
catform = CatForm(initial={'pictureid': instance.id})
For GET request, you don't need to override the __init__ method.
But, if you want to use the Catform in POST request, to use the value of pictureid somewhere else(lets say in save method), then you will need to override __init__ method here.
class CatForm(forms.ModelForm):
pictureid = forms.CharField()
class Meta:
model = Cat
fields = ['name']
def __init__(self, *args, **kwargs):
picid = kwargs.pop("pictureid")
print(picid)
super(CatForm, self).__init__(*args, **kwargs)
self.pictureid = picid
def save(self, *args, **kwargs):
print(self.pictureid) # if you want to use it in save method
return super().save(*args, **kwargs)

Django passing arguments through save methods (models)

i have models in django like this:
class Client(models.Model):
type = (choices)
class Bill(models.Model):
client = models.ForeignKey(Client)
class Detail(models.Model):
total = models.MoneyField() # i used currency package
bill = models.ForeignKey(Bill)
Detail class contains sales detail for the Bill, i already made a transaction to save bill and details at the same time in Bill.save() method but i want to pass Client.type from Bill.save() to Detail.Save(), i want something like that
def save(self, *args, **kwargs): #this is Bill save method
client = self.Client
transaction.atomic:
super(Bill, self).save(*args, **kwargs)
for detail in self.details
detail.save(client)
def save(self, *args, **kwargs): #this is Detail save method
self.pricing(client)
super(Detail, self).save(*args, **kwargs)
def pricing(self, client):
if client.type = 'value1':
self.total = self.total - (self.total*5/100)
elif client.type = 'value2':
self.total = self.total - (self.total*7/100)
else:
self.total = self.total - (self.total*10/100)
i don't know how passing arguments works on python and Django, what is the cleanest solution to solve this problem? in short i want the bill.save method to pick the client.type value and passe it through detail.save to calculate total with cases.
Thanks
You can pass the parameter in kwargs in save method try the following code to pass the client variable (detail.save(client=client)) in another save method and access this variable by using client = kwargs.get('client')
def save(self, *args, **kwargs): #this is Bill save method
client = self.Client
transaction.atomic:
super(Bill, self).save(*args, **kwargs)
for detail in self.details
detail.save(client=client)
def save(self, *args, **kwargs): #this is Detail save method
client = kwargs.get('client')
self.pricing(client)
super(Detail, self).save(*args, **kwargs)
May be this helps you..
In your case, you can simple use self property:
def save(self, *args, **kwargs): #this is Detail save method
self.pricing(self.bill.client)
# ^^^^^^^^^^^
super(Detail, self).save(*args, **kwargs)

Overriding save method to create second auto incrementing field in Django

I am trying to override the save method on a model in order to generate a unique, second auto-incrementing id.
I create my class and override the save() method, but for some reason it is erroring out with the following error:
TypeError: %d format: a number is required, not NoneType
Here's the code:
class Person(models.Model):
target = models.OneToOneField(Target)
person = models.OneToOneField(User)
gender = models.CharField(max_length=1)
gender_name = models.CharField(max_length=100)
person_id = models.CharField(max_length=100)
def save(self, *args, **kwargs):
self.person_id = "%07d" % self.id
super(Person, self).save(*args, **kwargs)
Is it because I didn't pass an id parameter and it hasn't saved yet? Is there anyway to generate a value from the id?
Safest and easiest way to achieve what you want is to use a post_save signal because it is fired right after save is called, but before the transaction is committed to the database.
from django.dispatch import receiver
from django.db.models.signals import post_save
#receiver(post_save, sender=Person)
def set_person_id(sender, instance, created, **kwargs):
if created:
instance.person_id = "%07d" % instance.id
instance.save()
Yes, self.id will be Nonein some cases, and then the assignment will fail.
However you cannot just the assignment and the call to super, as suggested in the comments, because then you wouldn't be persisting the assignment to the database layer.
You need to check whether the model has an id and then proceed differently:
def save(self, *args, **kwargs):
if not self.id: # Upon instance creation
super(Person, self).save(*args, **kwargs) # Acquire an ID
self.person_id = "%07d" % self.id # Set the person_id
return super(Person, self).save(*args, **kwargs)
This issues two save operations to the database. You will want to wrap them in a transaction to make sure your database receives these two fields simultaneously.
from django.db import IntegrityError, transaction
class Person(models.Model):
target = models.OneToOneField(Target)
person = models.OneToOneField(User)
gender = models.CharField(max_length=1)
gender_name = models.CharField(max_length=100)
person_id = models.CharField(max_length=100)
def create_person_id(self):
if not self.id: # Upon instance creation
super(Person, self).save(*args, **kwargs) # Acquire an ID
self.person_id = "%07d" % self.id
def save(self, *args, **kwargs):
try:
with transaction.atomic():
self.create_person_id
return super(Person, self).save(*args,**kwargs)
except IntegrityError:
raise # or deal with the error
I agree that signals might be the better option, if not, try using pk instead of id.
class Person(models.Model):
# [ . . . ]
def save(self, *args, **kwargs):
self.person_id = "%07d" % self.pk
super(Person, self).save(*args, **kwargs)

How to change input fields while overriding a django save() method

Suppose I had this following code:
class MyModel(models.Model):
...
...
def save(self, *args, **kwargs):
# pre-save edits can go here...
super(MyModel, self).save(*args, **kwargs)
When I create and save a model, MyModel(blah, blah, blah), there is a possibility that
one of the input fields is "None". In the overridden save method, the goal is to check for if a field is none, and if one is, change it to some other default value.
Are the input fields in args or kwargs? And is overriding save() even the proper way to do this?
I was thinking something like this:
def save(self, *args, **kwargs):
if 'username' in args and args['username'] is None:
args['username'] = some_default_value
super(MyModel, self).save(*args, **kwargs)
So where are the input params? args* or **kwargs, thank you.
I think it's better a pre_save signal to see if a input value is None:
from django.db import models
from django.db.models.signals import pre_save
from django.dispatch import receiver
class MyModel(models.Model):
field1 = models.TextField()
field2 = models.IntegerField()
#receiver(pre_save, sender=MyModel)
def mymodel_save_handler(sender, instance, *args, **kwargs):
if instance.field1 is None or instance.field1 == "":
instance.field1 = default_value
If you prefer override save method you can access to the model fields with self
def save(self, *args, **kwargs):
if self.username is None:
self.username = some_default_value
super(MyModel, self).save(*args, **kwargs)

Categories