Django form, change default max_length - python

Within the model "Example" I have two fields:
-"description" (varchar, length 64).
-"description_length" integer.
description_length has different value for each record, and it is always between 1 and 64.
In a Django form how is possible set the maxlength description equal to the description_length variable value?

Sure thing.
class ExampleForm(forms.ModelForm):
class Meta:
model = Example
def __init__(self, *args, **kwargs):
super(ExampleForm, self).__init__(*args, **kwargs)
if self.instance:
self.fields['description'].max_length = self.instance.description_length
Just be sure your description model field is big enough to accomodate the text of description_length:
class Example(models.Model):
MAX_DESCRIPTION_LENGTH = 1024
description = models.CharField(max_length=MAX_DESCRIPTION_LENGTH)
description_length = models.IntegerField(
validators=MaxValueValidator(MAX_DESCRIPTION_LENGTH))

Related

Django Form field initial value when updating an instance

I have a custom Django ModelForm that I use to update a model instance.
This is the example model:
class MyModel(models.Model):
number = models.CharField(_("Number"), max_length=30, unique=True)
sent_date = models.DateField(_('Sent date'), null=True, blank=True)
When creating an instance I will pass only the number field, that is why I don't want the sent_date to be required.
Then I have a view that updates the sent_date field, using this custom form:
# Generic form updater
class MyModelUpdateForm(forms.ModelForm):
class Meta:
model = MyModel
fields = []
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# Make fields mandatory
if hasattr(self, 'required_fields'):
for field_name in self.required_fields:
self.fields[field_name].required = True
# Set initial values
if hasattr(self, 'initial_values'):
for field_name, value in self.initial_values.items():
self.initial[field_name] = value
class SentForm(MyModelUpdateForm):
required_fields = ['sent_date']
initial_values = {'sent_date': datetime.date.today()}
class Meta(MyModelUpdateForm.Meta):
fields = ['sent_date']
field_classes = {'sent_date': MyCustomDateField}
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
MyModelUpdateForm is a generic ancestor for concrete forms like SentForm.
In my view whenever there is a GET I manually instantiate the form with:
my_form = SentForm({instance: my_model_instance})
So in this case I would expect the sent_date field to have an initial value set to today's date even tough the real model instance field is None.
If I inspect my_form object it does indeed have these attributes:
initial: {'sent_date': datetime.date(2018, 3, 1)}
instance: my_model_instance
fields: {'sent_date':
...: ...,
'initial': None # Why this is None?
...: ...
}
So apparently it should work but it doesn't: the field is always empty.
So I suspect that the value is coming from my_model_instance.sent_date that is in fact None.
The initial['sent_date'] = datetime.date(2018, 3, 1) is correct.
On the other side fields['sent_date']['initial'] = None it's not.
How can I always show the initial value when my_model_instance.sent_date is None?
Apparently I've solved with:
class MyModelUpdateForm(forms.ModelForm):
class Meta:
model = MyModel
fields = []
def __init__(self, *args, **kwargs):
initial = kwargs.get('initial', {})
if hasattr(self, 'initial_values') and not kwargs.get('data'):
for field_name, value in self.initial_values.items():
if not getattr(kwargs.get('instance', None), field_name, None):
initial[field_name] = value
kwargs.update({'initial': initial})
super().__init__(*args, **kwargs)
# Make fields mandatory
if hasattr(self, 'required_fields'):
for field_name in self.required_fields:
self.fields[field_name].required = True
Even tough it works I wouldn't mind a less hackish solution if anyone has any :)
I have this case in many places in my app without having any problem. However, I use a different way to set up initial value of some fields of an existing instance. Instead of:
self.initial[field_name] = value
I write, after having called super():
self.fields[field_name].initial = value
Can you try and tell the result ?

how to save one column value based on id column in django

I would like to save model id along with other format characters into another column, based on the model id being automatically created. For example,
For example, I have column name formated_id in shipment model, and want set its value to "easter" + ID which is automatically generated. How could I achieve that? Thanks.
class Shipment(models.Model):
formated_id = models.CharField("formatedid",max_length= 50)
class Meta:
ordering = ['-id']
def save(self, *args, **kwargs):
super().save(*args, **kwargs)
You would need to set the formated_id after the instance is first saved, which is when the primary key is created. Something along the lines of the following (untested) code should do the trick
class Shipment(models.Model):
formated_id = models.CharField("formatedid",max_length= 50)
class Meta:
ordering = ['-id']
def save(self, *args, **kwargs):
super().save(*args, **kwargs)
self.formated_id = 'easter{}'.format(self.pk)
super().save(*args, **kwargs)

Django - MultiChoiceField to_field_name and selected

I am currently using a MultiChoiceField to represent the corresponding ManyToManyField of my field. It works fine.
I updated my form to use the attribute to_field_name in the form field to change the field used for the values of the inputs and it works fine too.
My issue is that Django, to choose the selected data of the form field, is using the pk and not the field given in to_field_name. Any clue ?
class Model1(models.Model):
name = models.Charfield(...)
muscles = models.ManyToManyField(Model2, blank=True, null=True,)
class Model2(models.Model):
name = models.CharField(...)
field1 = models.CharField(...)
class MyForm(ModelForm):
muscles = forms.ModelMultipleChoiceField(
queryset=None,
to_field_name="field1", required=False,)
def __init__(self, *args, **kwargs):
super(MyForm, self).__init__(*args, **kwargs)
self.fields['muscles'].queryset = Model2.objects.all()
The field of the model is a standard ManyToManyField.
As a example :
In model2, I have these elements : {pk=1, name=name1, field1=3}, {pk=2, name=name2, field1=1}, {pk=3, name=name3, field1=2}.
the generated select is:
<option value=3>name1</option>
<option value=1>name2</option>
<option value=2>name3</option>
If I select name2, the right pk (ie 2) is saved in database.
When the form is displayed, the selected option is name3 (ie value 2) instead of name1.
I hope I have been clear enough.
Thanks for your help !
It looks as if you might have hit this bug, which is fixed in Django 1.10.
Well, I managed to achieve what I wanted. As suggested by #Alasdair, I created an intermediary model.
class Model1(models.Model):
name = models.Charfield(...)
muscles = models.ManyToManyField(Model2, blank=True, null=True,)
class Model2(models.Model):
name = models.CharField(...)
field1 = models.CharField(...)
class Model1Model2(models.Model):
model1 = models.ForeignKey(Model1)
model2 = models.ForeignKey(Model2)
class MyForm(ModelForm):
muscles = forms.ModelMultipleChoiceField(
queryset=None,
to_field_name="field1", required=False,)
def __init__(self, *args, **kwargs):
super(MyForm, self).__init__(*args, **kwargs)
self.fields['muscles'].queryset = Model2.objects.all()
Then in the save_model method, I can go through the values of my field and saves them.
The main point to solve my problem was clearly the intermediary model.
Thanks #Alasdair

Why is "blank" is missing in django.forms.CharField, but present in django.db.models.CharField?

Background
I have a model with two fields that are set the blank:
class News(models.Model):
title = models.CharField(max_length = 50, blank = True)
info = models.TextField(blank = True)
The thing is that I want to set the max_length dynamically when the form is built, so I have a custom form:
class NewsForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(NewsForm, self).__init__(*args, **kwargs)
title_max_length = 20
info_max_length = 100
self.fields["title"] = forms.CharField(max_length = title_max_length)
self.fields["info"] = forms.CharField(
widget = forms.Textarea,
validators = [
MaxLengthValidator(info_max_length)
]
)
Note: Those two length values are actually fetched from the database, but I chose not to include that code to keep the examples shorter.
The problem
When I'm using those custom fields the blank option is overwritten/ignored.
I tried just adding the max_length, widget and validators to the existing fields, like this:
class NewsForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(NewsForm, self).__init__(*args, **kwargs)
title_max_length = 20
info_max_length = 100
self.fields["title"].max_length = title_max_length
self.fields["info"].widget = forms.Textarea
self.fields["info"].validators = [MaxLengthValidator(info_max_length)]
When doing this the blank option works, but the dynamic max_length is not applied to the form.
I tried to look in the django source, but I'm quite new so it's too much to take in right now.
Is there some way I can achieve this?
When creating your form, add the following parameter to CharField apart from the max_length, widget and validators:
forms.CharField(...,required = False)
In Django, blank=True in models correlates to required=False in forms.

Store form fields as key-values / individual rows

I have a simple form in Django that looks like this:
class SettingForm(forms.Form):
theme = forms.CharField(rrequired=True,
initial='multgi'
)
defaultinputmessage = forms.CharField(required=True,
initial='Type here to begin..'
)
...and the model to store it looks like:
class Setting(models.Model):
name = models.CharField(
null=False, max_length=255
)
value= models.CharField(
null=False, max_length=255
)
When the form is submitted, how can i store the form fields as key value pairs and then when the page is rendered, how can I initialize the form with the key's value. I've tried looking for an implementation of this but have been unable to find one.
Any help?
Thanks.
I'm assuming you want to store 'theme' as the name and the value as the value, same for defaultinputmessage. If that's the case, this should work:
form = SettingForm({'theme': 'sometheme', 'defaultinputmessage': 'hello'})
if form.is_valid():
for key in form.fields.keys():
setting = Setting.objects.create(name=key, value=form.cleaned_data[key])
Here's how I did it.
I needed to do this because I had a Model that stored information as key value pairs and I needed to build a ModelForm on that Model but the ModelForm should display the key-value pairs as fields i.e. pivot the rows to columns. By default, the get() method of the Model always returns a Model instance of itself and I needed to use a custom Model. Here's what my key-value pair model looked like:
class Setting(models.Model):
domain = models.ForeignKey(Domain)
name = models.CharField(null=False, max_length=255)
value = models.CharField(null=False, max_length=255)
objects = SettingManager()
I built a custom manager on this to override the get() method:
class SettingManager(models.Manager):
def get(self, *args, **kwargs):
from modules.customer.proxies import *
from modules.customer.models import *
object = type('DomainSettings', (SettingProxy,), {'__module__' : 'modules.customer'})()
for pair in self.filter(*args, **kwargs): setattr(object, pair.name, pair.value)
setattr(object, 'domain', Domain.objects.get(id=int(kwargs['domain__exact'])))
return object
This Manager would instantiate an instance of this abstract model. (Abstract models don't have tables so Django doesn't throw up errors)
class SettingProxy(models.Model):
domain = models.ForeignKey(Domain, null=False, verbose_name="Domain")
theme = models.CharField(null=False, default='mytheme', max_length=16)
message = models.CharField(null=False, default='Waddup', max_length=64)
class Meta:
abstract = True
def __init__(self, *args, **kwargs):
super(SettingProxy, self).__init__(*args, **kwargs)
for field in self._meta.fields:
if isinstance(field, models.AutoField):
del field
def save(self, *args, **kwargs):
with transaction.commit_on_success():
Setting.objects.filter(domain=self.domain).delete()
for field in self._meta.fields:
if isinstance(field, models.ForeignKey) or isinstance(field, models.AutoField):
continue
else:
print field.name + ': ' + field.value_to_string(self)
Setting.objects.create(domain=self.domain,
name=field.name, value=field.value_to_string(self)
)
This proxy has all the fields that I'd like display in my ModelFom and store as key-value pairs in my model. Now if I ever needed to add more fields, I could simply modify this abstract model and not have to edit the actual model itself. Now that I have a model, I can simply build a ModelForm on it like so:
class SettingsForm(forms.ModelForm):
class Meta:
model = SettingProxy
exclude = ('domain',)
def save(self, domain, *args, **kwargs):
print self.cleaned_data
commit = kwargs.get('commit', True)
kwargs['commit'] = False
setting = super(SettingsForm, self).save(*args, **kwargs)
setting.domain = domain
if commit:
setting.save()
return setting
I hope this helps. It required a lot of digging through the API docs to figure this out.

Categories

Resources