Populating field values in django forms - django

I want to know the best way to assign a generated value for model field in a django form. Here is the code for what I need to implement the logic.
Model:
class Booking:
number = models.CharField(max_length=8)
category = models.CharField(max_length=2)
ref = models.CharField(max_length=10)
What I need to do is, store the combination of number + category in ref field when model is saved. I know there are two methods called save() and clean() available for this. But I'm not sure which one is the best to use.
Thanks.

You can do this with a custom save function in the Booking model.
class Booking(models.Model):
...
def save(self, *args, **kwargs):
self.ref = "%s%s" % (self.number, self.category)
super(Booking, self).save(*args, **kwargs)

Related

Django how to add a field to a model that relies on the pk being created first

I am trying to add a hashid that is based on the pk of my model
class ExampleModel(models.Model):
hash_id = models.CharField(max_length=30)
How the hash_id is created.. Assume 15454 is the pk of my object.
from hashids import Hashids
hashids = Hashids(salt='example salt')
hashids.encode(15454)
'Eo6v'
The main reason I want to store the hash_id in the model is because I don't want to recompute it everytime or if my salt changes in the future for some reason.
Is there a way to automatically generate this field when the object is created since it needs the PK to be created first?
How about to override in save model? like:
class ExampleModel(models.Model):
hash_id = models.CharField(max_length=30)
def save(self, *args, **kwargs):
is_create = self.pk is None
super().save(*args, **kwargs)
# We got pk after save above.
if is_create:
hashids = Hashids(salt='example salt')
self.hash_id = hashids.encode(self.pk)
super().save(update_fields=["hash_id"])

Trying to do a calculation on django models

i have two integer fields that i want to divide to get the value of 3rd field.
#property
def Pallets_Count(self):
return self.CASES/self.CasesPerPallet
but the result in the database always shows null .
#property
def Pallets_Count(self):
return self.CASES/self.CasesPerPallet
#property will not save anything into your model field. It works like a method. You can call in in your template like a model field mypost.Pallets_Count.
If you want to put the result into a database field, you need to override save method. But this might not be necessary. Property most likely is enough.
class MyModel(models.Model):
# your other fields
fieldname = models.FloatField()
def save(self, *args, **kwargs):
self.fieldname = self.CASES/self.CasesPerPallet
super(MyModel, self).save(*args, **kwargs)

How to save an instance and update a reverse M2M field in a modelForm

Lots of questions cover saving instances before M2M relationships, however I have a problem achieving this when saving via a form with a _set relationship, which does not appear to be covered in other answers.
I have two models with an M2M relationship:
class Author(models.Model):
name = models.CharField()
class Publisher(models.Model):
authors = models.ManyToManyField(Author)
When creating a new Author, I want to be able to set Publishers for that Author. Currently I handle this in the form:
class AuthorForm(forms.ModelForm):
publishers = forms.ModelMultipleChoiceField(queryset=Publisher.objects.all())
def __init__(self, *args, **kwargs):
super(AuthorForm, self).__init__(*args, **kwargs)
# If the form is used in a UpdateView, set the initial values
if self.instance.id:
self.fields['publishers'].initial = self.instance.publisher_set.all(
).values_list('id', flat=True)
def save(self, *args, **kwargs):
instance = super(AuthorForm, self).save(*args, **kwargs)
# Update the M2M relationship
instance.issuer_set.set(self.cleaned_data['issuers'])
return instance
class Meta:
model = Author
fields = ['name', 'publishers']
This works fine when updating an existing Author. However, when I use it to create a new one I get:
<Author: Sample Author> needs to have a value for field "id" before this many-to-many relationship can be used.
I understand this is because the instance needs to be saved first, however in def save() I am doing this (instance = super(AuthorForm, self).save(*args, **kwargs)).
Where am I going wrong?
calling super does not guarantee that it will create an instance. Because, form's save(...) method has a argument called commit, if the method calling the save is passing commit=False(for example: form.save(commit=False)), then it will not save the instance. So you need to explicitly save it. Like this:
instance = super(AuthorForm, self).save(commit=True)

Override save method to add list in the many to many field

I have this model to save post from the users:
class Tag(models.Model):
name = models.CharField(max_length=255, unique=True)
def add_tags(obj_id, body):
object = Post.objects.get(id=obj_id)
tag_list = [Tag.objects.create(name=word) for word in body.split()]
for tag in tag_list:
object.tags.add(tag)
class Post(models.Model):
user = models.ForeignKey(User)
body = models.TextField()
tags = models.ManyToManyField(Tag, blank=True)
pub_date = models.DateTimeField(default=timezone.now)
activity = GenericRelation(Activity, related_query_name="posts")
def save(self, *args, **kwargs):
super(Post, self).save(*args, **kwargs)
if self.body:
body = self.body
obj_id = self.id
add_tags(obj_id, body)
So whenever a user post something, I would like to check if there's any hash-tag used inside the body. If there are tags, then fetch the tags inside the list.
But when I am posting, the tag objects are created, but they are not adding for the Post.tags fields.
post.body example = Check #from the http://somesitedotcom/page#idtop #hell yeah!
What am I doing wrong here?
This is a common mistake made by people. In order to update a model object's m2m relationship, the object needs to be saved in database first. Django m2m is essentially using an intermediate table to store the relationship, so without both ends saved, the relationship cannot be formed.
I wouldn't rely on overriding save() method to achieve this, but rather deal with this process separately in views method, because save() can happen in any situation, but you might not always what this process to happen. For the sake of correctness, you should do it after you call super(Post, self).save(*args, **kwargs) in the save method.
Edit:
Sounds like your code is also buggy. tag_list is a list of strings, but your code self.tags.add(*tag_list) treated them as Tag objects. You need to create Tag objects then use add to link the relationship.
You can’t associate Post with a tag until it’s been saved:
def save(self, *args, **kwargs):
super(Post, self).save(*args, **kwargs)
if self.body:
tag_list = [word for word in self.body.split() if word.startswith("#")]
self.body = tag_list
self.tags.add(*tag_list)

Setting value of Django field inside of Form class

I'm trying to set the value of a Django field inside of the Form class. Here is my model
class Workout(models.Model):
user = models.ForeignKey(User , db_column='userid')
datesubmitted = models.DateField()
workoutdate = models.DateField();
bodyweight = models.FloatField(null=True);
workoutname = models.CharField(max_length=250)
Here is the form class, in which i am attempting to achieve this:
class WorkoutForm(forms.ModelForm):
class Meta:
model = Workout
def __init__(self,*args, **kwargs):
# this is obviously wrong, I don't know what variable to set self.data to
self.datesubmitted = self.data['datesubmitted']
Ok, sorry guys. I'm passing the request.POST data to the WorkoutForm in my view like this
w = WorkoutForm(request.POST)
However, unfortunately the names of the html elements have different names then the names of the model. For instance, there is no date submitted field in the html. This is effectively a time stamp that is produced and saved in the database.
So I need to be able to save it inside the form class some how, I think.
That is why I am trying to set the datesubmitted field to datetime.datetime.now()
Basically I am using the form class to make the verification easier, and I AM NOT using the form for it's html output, which I completely disregard.
You have to do that in the save method of your form
class WorkoutForm(forms.ModelForm):
class Meta:
model = Workout
def __init__(self,*args, **kwargs):
super(WorkoutForm, self).__init__(*args, **kwargs)
def save(self, *args, **kw):
instance = super(WorkoutForm, self).save(commit=False)
instance.datesubmitted = datetime.now()
instance.save()
How ever you can set that in your model also to save the current datetime when ever a new object is created:
datesubmitted = models.DateTimeField(auto_now_add=True)
You can set some extra values set in form as:
form = WorkOutForm(curr_datetime = datetime.datetime.now(), request.POST) # passing datetime as a keyword argument
then in form get and set it:
def __init__(self,*args, **kwargs):
self.curr_datetime = kwargs.pop('curr_datetime')
super(WorkoutForm, self).__init__(*args, **kwargs)
You should not be using a ModelForm for this. Use a normal Form, and either in the view or in a method create a new model instance, copy the values, and return the model instance.