On Django Forms, how do I specify a default value for a field if the user leaves it blank? Initial sets an initial value, but the user can delete this.
If you're using a ModelForm simply follow Dan's advice.
If however you're simply using a Form then you may have to specify how to deal with validation. Example Documentation
class YourForm(forms.Form):
...
def clean_field(self):
data = self.cleaned_data['field']
if not data:
data = 'default value'
return data
Set a default value in the model.
class YourModel(models.Model):
this_field = models.TextField(default="default_value")
WallyBay's answer works for me.
(+adding my experience)
If you leave the form field empty, None value will be passed.
you can check this by printing out from.cleaned_data().
But, in my case, None value wasn't replaced with the Model's default value.
I tested by creating objects in the shell('python manage.py shell')
passing 1)None value and 2)empty parameter.
My Model:
class Cart(models.Model):
total_amount = models.DecimalField(decimal_places=2, max_digits=1000, default=1, blank=True, null=True)
quantity = models.IntegerField(default=1, blank=True)
note = models.CharField(max_length=300, default='nothing to mention')
summery = models.TextField(blank=False, null=True)
event_won = models.BooleanField(default=False)
My Form:
class CartForm(forms.ModelForm):
summery = forms.CharField()
total_amount = forms.DecimalField(required=False)
quantity = forms.IntegerField(initial=20)
note = forms.CharField(widget=forms.TextInput(attrs={"placeholder":"write a note"}))
class Meta:
model = Cart
fields = [
'summery',
'total_amount',
'quantity',
'note'
]
1) create the object by passing None value.
Cart.objects.create(total_amount=None)
Result: Default value didn't apply. The total_amount is Null(None).
2) create the object without passing any.
Cart.objects.create()
Result: default value worked. total_amount is 1.
When I delete null=True option for the total_amount in the Model class, it gives me an error 'NOT NULL constraint failed'
Related
I'm trying to save data to my database using ModelSerializer but when I pass a ForeignKey instance, it gets converted to string or integer type from instance type after calling is_valid() and this is the error I get:
ValueError at /app/core/create-trip
Cannot assign "'ChIJTWE_0BtawokRVJNGH5RS448'": "CityAttrRtTb.attr_nm" must be a "CityAttrn" instance.
The error above shows that the CityAttrn instance was converted to its respective field value(and thus its type changed) after calling is_valid().
My problem is basically the same as this question
The question I linked to above has an answer that I need to pass the foreign key as an argument to the save() method of the serializer only after calling the is_valid() method.
if serializer.is_valid():
serializer.save(fk=foreign_key_instance)
In this example, I have one data and since I passed the foreign key after is_valid is called, there seems to be no problem. I can do that if I only have one data. However, I am trying to save multiple data at once with .save(many=True) and each of those data have a different foreign key instance. How do I save those kinds of data?
models.py
class CityAttraction(models.Model):
cty_attr_id = models.AutoField(primary_key=True)
cty_nm = models.CharField(max_length=200)
attr_nm = models.CharField(max_length=200, unique=True)
des = models.CharField(max_length=200, blank=True, null=True)
class Meta:
managed = False
db_table = 'city_attrn'
unique_together = (('cty_nm', 'attr_nm'),)
class CityAttractionRating(models.Model):
cty_nm = models.CharField(max_length=200)
attr_nm = models.ForeignKey('CityAttrn', on_delete=models.CASCADE, db_column='attr_nm', primary_key=True, to_field='attr_nm')
rt_src = models.IntegerField(blank=True, null=True)
class Meta:
managed = False
db_table = 'city_attr_rt_tb'
unique_together = (('cty_nm', 'attr_nm', 'rt_src'),)
views.py
def some_calculation(city_attraction):
final_result = []
for cityattraction_instance in city_attraction:
print(attraction)
rating_dict["cty_nm"] = "TEST"
rating_dict["attr_nm"] = cityattraction_instance # its type is CityAttraction instance right now but it gets changed to string after calling is_valid()
rating_dict["rt_src"] = 1
final_result.append(rating_dict.copy())
return final_result
city_attraction = CityAttractionSerializer(data=request.data, many=True) if city_attraction.is_valid():
city_attraction = city_attraction.save()
calculated_data = some_calculations(city_attraction)
integer_rated_attraction = CityAttractionRatingSerializer(data=calculated_data, many=True)
if integer_rated_attraction.is_valid():
integer_rated_attraction.save() #how do I save all the instances of CityAttraction here without changing its type to string or integer from "instance"?
In the above view, city_attraction is the list of all CityAttraction instances. I need to pass it to the arguments of integer_rated_attraction.save() so as to not lose the "instance" type because of is_valid(). How do I do it?
serializers.py
class CityAttractionSerializer(serializers.ModelSerializer):
class Meta:
model = CityAttraction
fields = '__all__'
class CityAttractionRtSerializer(serializers.ModelSerializer):
class Meta:
model = CityAttractionRating
fields = '__all__'
When I pass in the CityAttraction instance to some_calculation function and then performing the validation and saving, I was expecting the model to be saved. But, because calling the is_valid() function changed the CityAttr to the field value("ChIJTWE_0BtawokRVJNGH5RS448" in this case), I got an Value Error exception shown at the top. How would I go about solving this? Thank you!
You have mistake in save.
serializer.save(fk=foreign_key_instance) =>
serializer.save(pk=foreign_key_instance)
What is the best way to limit only one record to be default in django
I have a model where i have a flag for default
class BOMVersion(models.Model):
name = models.CharField(max_length=200,null=True, blank=True)
material = models.ForeignKey(Material)
is_default = models.BooleanField(default=False)
I want to have only one value to be default for the same material but this material can have a lot of non default ones.
It was odd that this question was not addressed more often. If I have a default record, I want to record that in the class as a member variable. And to determine if an instance is the default, I want to compare the class default member variable with the instance id. Unfortunately I could not figure out how to access class variables and instance variables nicely in the same class function in Python (may be someone can comment), but this does not require to hit the database for a default or store a bunch of records pointing to a default. Just one member variable in the class.
After I wrote this, I realized every time the application is restarted, default is reset to None, so you will have to store this in a database. I have updated my answer accordingly. However, checking that the member variable is not null, and only hitting the database if it is would reduce hits here. The model I used was:
class RecordOfInterest(models.Model):
"""
Record Records of interest here. Stores ID, and identifying character
"""
# asume maximum 64 character limit for the model.
model_name = models.CharField(max_length=64, unique=True)
record_no = models.IntegerField()
human_ident = models.CharField(max_length=64, help_text='How is this of interest')
# Id it as default, deposit, ... Don't bother indexing, as there will only be a few
def __unicode__(self):
return u'Model %s record %d for %s' % (self.model_name, self.record_no, self.human_ident)
class Meta:
unique_together = ('model_name', 'human_ident')
class Product(models.Model):
"""
Allow one product record to be the default using "Product.default = prod_instance"
check default with "Product.is_default(prod_instance)"
"""
default = None # set this to id of the default record
cart_heading = models.CharField(max_length=64, blank=True)
country = CountryField()
pricing = models.ForeignKey(
'Price', blank=True, null=True, related_name='visas', on_delete=models.SET_NULL)
#classmethod
def is_default(cls, obj):
if cls.default_no == None:
try:
cls.default_no = RecordOfInterest.objects.get(model_name=cls.__name__, human_ident='default')
except RecordOfInterest.DoesNotExist:
default_no = None
return cls.default_no == obj.id
#classmethod
def set_default(cls, obj):
try:
default_rec = RecordOfInterest.objects.get(model_name=cls.__name__, human_ident='default')
except RecordOfInterest.DoesNotExist:
RecordOfInterest.objects.create(model_name=cls.__name__, record_no=obj.id, human_ident='default')
else:
if default_rec.record_no != obj.id:
default_rec.record_no = obj.id
default_rec.save()
cls.default_no = obj.id
return
Saving the ID in settings.py if it is static.
Save it into a separate "default" table with one record (or use the most recent) if it's dynamic.
Save the default in another table like this:
class BOMVersion(models.Model):
name = models.CharField(max_length=200,null=True, blank=True)
material = models.ForeignKey(Material)
class BOMVersionDefault(model.Models)
time_set= models.Datetime(auto_created=True)
default_material = models.ForiegnKey(Material)
To query:
default = BOMVerDefault.objects.latest(time_set).get().default_material
If you have several material types that each need a default then default_material would be a field in a material-type table.
Getting one record to be default in a table is most basic requirement we developer come face to face, after spending couple of hours over it, i think a neat and clean solution in django would be update all records to default false if current form instance has default value to be "true" and then save the record.
class FeeLevelRate(TimeStampedModel):
"""
Stores the all the fee rates depend on feelevel
"""
feelevel = models.ForeignKey(FeeLevel, on_delete= models.PROTECT)
firstconsultfee = models.DecimalField(_('First Consultation Charges'),max_digits=10,decimal_places=2,blank=True)
medcharges = models.DecimalField(_('Medicines Charges per Day'),max_digits=10,decimal_places=2,blank=True)
startdate = models.DateField(_("Start Date "), default=datetime.date.today)
default_level = models.BooleanField(_('Is Default Level?'),default=False)
class Meta:
constraints = [
models.UniqueConstraint(fields=["feelevel","startdate"], name='unique_level_per_date'),
]
def __str__(self):
return "%s applicable from (%s)" % ( self.feelevel, self.startdate.strftime("%d/%m/%Y"))
class FeeLevelRateCreate(CreateView):
model = FeeLevelRate
fields = ['feelevel', 'firstconsultfee', 'medcharges', 'startdate', 'default_level']
context_object_name = 'categories'
success_url = reverse_lazy('patadd:feerate_list')
def form_valid(self, form):
# Update all the default_level with false.
#UserAddress.objects.filter(sendcard=True).update(sendcard=False)
if form.instance.default_level:
FeeLevelRate.objects.filter(default_level=True).update(default_level=False)
return super().form_valid(form)
So here is the problem. I got 2 models:
RefereeLevel and Referee
Here is both:
class RefereeLevel(models.Model):
level = models.PositiveSmallIntegerField(blank=False,default=1,verbose_name=_("level"),unique=True)
salary = models.DecimalField(blank=False,default=0.00,decimal_places=2,max_digits=4,verbose_name=_("salary"))
def __unicode__(self): # Python 2.7: def __unicode__(self):
return self.level
And the second class:
class Referee(models.Model):
member = models.OneToOneField(Member,related_name='member',blank=False)
information = models.ForeignKey(RefereeLevel,related_name='information',blank=False,null=True,default=1)
What happens now is that if I delete a RefereeLevel, the Referee with that level is deleted. But I don't want that, I want the Referee's information be set to none.
Is that possible?
Thanks,
Ara
You need to set the on_delete parameter.
In your case :
information = models.ForeignKey(RefereeLevel,related_name='information',blank=False,null=True,default=1, on_delete=models.SET_NULL
What you can do is to set on_delete param like
information = models.ForeignKey(RefereeLevel,related_name='information',blank=False,null=True,default=1, on_delete=models.SET_NULL)
on_delete=models.SET_NULL will set the foreign key value to null
I've been trying to reset my database and figure out why this is happening, but for some reason I'm getting this error:
IntegrityError: main_funding_rounds_investments.investment_id may not be NULL
I can't figure out how or why the automatic id field would be null? If anyone has ideas I would greatly appreciate it!
When i check the db with inspectdb, I get the following:
class MainFundingRoundsInvestments(models.Model):
id = models.IntegerField(primary_key=True)
funding_rounds_id = models.IntegerField()
investment_id = models.IntegerField()
class Meta:
db_table = u'main_funding_rounds_investments'
and
class MainInvestment(models.Model):
id = models.IntegerField(primary_key=True)
class Meta:
db_table = u'main_investment'
Here are my models:
#funding rounds
class funding_rounds(models.Model):
funded_day = models.IntegerField(null=True)
investments = models.ManyToManyField("Investment")
class Investment(models.Model):
company = models.ManyToManyField("Company", null=True, related_name ="Investments_company")
financial_org = models.ManyToManyField("Financial_org", null=True, related_name ="Investments_financial_org")
person = models.ManyToManyField("Person", null=True, related_name ="Investments_person")
This is where I create the objects:
def save_dict_to_db_R(model_class ,dicta):
testCo = model_class()
for key,value in dicta.items():
try:
field = model_class._meta.get_field(key)
if isinstance(field, models.ManyToManyField):
continue
else:
if model_class._meta.get_field(key):
print("i == something:" + key + " ")
setattr(testCo, key, value)
except FieldDoesNotExist:
continue
testCo.save(
for field in model_class._meta.many_to_many:
if field.name in dicta and hasattr(dicta[field.name], 'append'):
for obj in dicta[field.name]:
rel_instance = save_dict_to_db_R(field.rel.to, obj)
getattr(testCo, field.name).add(rel_instance)
Why have you set the primary key field manually? Unless you need to call the field something different from id, or give it different attributes, you should just leave it out of the model and let Django add it automatically.
Your immediate problem is that you've used IntegerField instead of AutoField, but if you removed your definition automatically that would be done for you.
Try using AutoField
I am using a ModelForm to create a form, and I have gotten the initial values set for every field in the form except for the one that is a ManyToMany field.
I understand that I need to give it a list, but I can't get it to work. My code in my view right now is:
userProfile = request.user.get_profile()
employer = userProfile.employer
bar_memberships = userProfile.barmembership.all()
profileForm = ProfileForm(
initial = {'employer': employer, 'barmembership' : bar_memberships})
But that doesn't work. Am I missing something here?
Per request in the comments, here's the relevant parts of my model:
# a class where bar memberships are held and handled.
class BarMembership(models.Model):
barMembershipUUID = models.AutoField("a unique ID for each bar membership",
primary_key=True)
barMembership = USStateField("the two letter state abbreviation of a bar membership")
def __unicode__(self):
return self.get_barMembership_display()
class Meta:
verbose_name = "bar membership"
db_table = "BarMembership"
ordering = ["barMembership"]
And the user profile that's being extended:
# a class to extend the User class with the fields we need.
class UserProfile(models.Model):
userProfileUUID = models.AutoField("a unique ID for each user profile",
primary_key=True)
user = models.ForeignKey(User,
verbose_name="the user this model extends",
unique=True)
employer = models.CharField("the user's employer",
max_length=100,
blank=True)
barmembership = models.ManyToManyField(BarMembership,
verbose_name="the bar memberships held by the user",
blank=True,
null=True)
Hope this helps.
OK, I finally figured this out. Good lord, sometimes the solutions are way too easy.
I need to be doing:
profileForm = ProfileForm(instance = userProfile)
I made that change, and now everything works.
Although the answer by mlissner might work in some cases, I do not think it is what you want. The keyword "instance" is meant for updating an existing record.
Referring to your attempt to use the keyword "initial", just change the line to:
bar_memberships = userProfile.barmembership.all().values_list('pk', flat=True)
I have not tested this with your code, but I use something similar in my code and it works.