the below form + view works fine
forms.py
class FormGlobalSettings(forms.Form):
global_font_family = forms.TypedChoiceField(required=False, label='Font Family', choices=choices_font_family, empty_value=None)
views.py
def main_view(request):
if request.method != 'POST':
form_global_settings = FormGlobalSettings()
else:
form_global_settings = FormGlobalSettings(data=request.POST)
if all([form_global_settings.is_valid()]):
cleaned_data = form_global_settings.cleaned_data
nested_data = {"fontFamily": cleaned_data3['global_font_family']}
return JsonResponse(nested_data)
return render(request, 'pbi_theme_app/index.html', {'form_global_settings': form_global_settings})
Output is:
{"fontFamily": "Arial"}
However, what I am trying to achieve is, that sometimes the POST request is blank/empty/null, as the user doesn't want to have any value in this field. The choices for the global_font_family are set up in this manner:
choices_font_family = [(None, ""), ("Arial", "Arial"), etc..]
Meaning that the if the user leaves the field empty, it results into None. This results into having it in the json as null:
{"fontFamily": null}
Now what I am trying to achieve, and because I have hardcoded "fontFamily" into the jsonresponse, I want the whole key, value pair to be gone if the user decides to have the field empty, meaning that the whole key, value pair "fontFamily: null is gone from the jsonresponse.
To summarize: I need to somehow make the key in the json dynamic, and when the POST request is empty, I want to leave the whole key, value pair from getting inputed into the json.
The intended behaviour is seen on the following webpage, when you download the theme and you didnt input anything it leaves the whole json code empty:
https://powerbi.tips/tools/report-theme-generator-v3/
Thank you :)
First of all, you don't need to include none in the choice list. You can simply declare null=True in your model.py class fields.
You can achieve the intended behavior by overwriting the intended field in your model.py class before saving the input
let assume I have the following class.
choices_font_family = [
("c1", "choice1"),
("c2", "choice2"),
("c3", "choice3"),
("c4", "choice4")
]
class Post(models.Model):
theme_type = models.CharField(null=True, max_length=255, verbose_name="some text", choices=choices_font_family )
....
....
def save(self, force_insert=False, force_update=False, *args, **kwargs):
if theme_type is None:
theme_type = '<some font>' # overwriting none to whatever you want it to be
super().save(force_insert, force_update, *args, **kwargs)
EDIT
forms.py
from django import forms
from .models import FormGlobalSettings
class UserdataModelForm(forms.ModelForm):
class Meta:
model = FormGlobalSettings
def clean(self):
return self.clean()
def __init__(self, *args, **kwargs):
self.request = kwargs.pop('request', None)
super(UserdataModelForm, self).__init__(*args, **kwargs)
models.py
from django.db import models
choices_font_family = [
("c1", "choice1"),
("c2", "choice2"),
("c3", "choice3"),
("c4", "choice4")
]
class FormGlobalSettings(models.Model):
theme_type = models.CharField(null=True, max_length=255, verbose_name="some text", choices=choices_font_family )
....
....
def save(self, force_insert=False, force_update=False, *args, **kwargs):
if theme_type is None:
theme_type = '<some font>' # overwriting none to whatever you want it to be
super().save(force_insert, force_update, *args, **kwargs)
Here is a link to Django docs on saving and overwriting save. And here is an example from Django docs on how to use forms and models.
If this solves your problem don't forget to accept this as the correct answer.
Related
I have two Models for my Project, 1. Category Model and 2. Course Model
Course Model has a Foreign Key reference with my Category Model as shown below.
class Category(models.Model):
categoryname = models.CharField(max_length=200,null=True,blank=True, default="")
class Courses(models.Model):
coursename = models.CharField(max_length=200,null=True,blank=True, default="")
course_category = models.ForeignKey(Category, related_name="courses", blank=True,null=True,on_delete=models.CASCADE)
logo = models.ImageField(upload_to='courselogos', null=True, blank=True)
Initially I was using HTML form and will be able to save the Course data under a Particular Category to the database as:
def add_course(request):
if request.method == 'POST':
course_name = request.POST.get('coursname')
categoryid = request.POST.get('category_id')
category = Category.object.get(id=category_id)
course_logo = request.FILES.get('logo')
course = Courses(coursename=course_name, course_category=category, logo= course_logo)
course.save()
return redirect('/all_category')
Later I decided to move on using Django Model forms and I tried to implement the code as follows
class AddCourseForm(forms.ModelForm):
class Meta:
model = Courses
fields = ('coursename', 'course_category', 'logo')
widgets = {
'coursename' : forms.TextInput(attrs={'class':'form-control'}),
}
def __init__(self, *args, **kwargs):
category_id = kwargs.pop('category_id',1)
super(AddCourseForm, self).__init__(*args, **kwargs)
self.fields['course_category']=forms.ModelChoiceField(widget=forms.TextInput(), queryset=Category.objects.filter(id=category_id))
Later in the view I have saved the data as
def add_course(request):
if request.method == 'POST':
addcourse = AddCourseForm(request.POST, request.FILES)
if addcourse.is_valid():
addcourse.save()
return redirect('/all_category')
On my HTML page I am passing the input to the 'course_category' inputfield as 1,2,3....etc as the category_id value
I have rendered the field in the form as
{{form.course_category}}
On Submitting the form when my 'course_category' inputfield has value as 1, it saves the data to the database but when the inputfield value is 2 then it is not even entering to the if condition of addcourse.is_valid() in the view function.
As I'm new the Django I'm not able to find the right way to get the ForeignKey value dynamically save the data in reference to that Category. Also I want to populate the same data back to the form in case of edit.
Please guide, thanks in advance.
After debugging the Code a little bit, I modified the init function in the AddCourseForm class as mentioned below that solved my issue but I am not it is the right way to do this or not
def __init__(self, *args, **kwargs):
category_id = None
for key in args[0:1]:
category_id = args[0].get('course_category')
super(AddCourseForm, self).__init__(*args, **kwargs)
self.fields['course_category']=forms.ModelChoiceField(widget=forms.TextInput(), queryset=Category.objects.filter(id=category_id))
I don't think doing this should be that difficult, here is how you would set the course_category options in the form normally:
# forms.py
class AddCourseForm(forms.ModelForm):
...
def __init__(self, *args, **kwargs):
self.course_categories = Category.objects.all()
super(AddCourseForm, self).__init__(*args, **kwargs)
self.fields['course_category'].queryset = self.course_categories
If you want to set a particular category in the form the you can pass an initial value in your view:
# views.py
def add_course(request, pk):
# note: you can pass the category primary key to your view, you need to
# specify this in your URLs and then your template
course_category = Category.objects.get(pk=pk)
form = AddCourseForm(initial={'course_category': course_category})
If you then want to kill all other options entirely, you can use the initial value to set your filter:
# forms.py
class AddCourseForm(forms.ModelForm):
...
def __init__(self, *args, **kwargs):
super(AddCourseForm, self).__init__(*args, **kwargs)
self.fields['course_category'].queryset = Category.objects.filter(
pk=self.fields['course_category'].initial)
In order to be able to automatically use the email sent by the wagtail email form, I need to put some tags in front of the generated lines.
So in short, a line inside the email should look like this: "#Start# descr: somethingsomething. #Ende#".
I managed to add a "start_tag" and a "end_tag" field to the wagtail "form creator" but now I get a error when I try to submit the form.
The admin form editor:
UPDATE
The new error:
NameError at /anforderungsformular/
name 'start_tag' is not defined
Request Method: POST
Request URL: http://localhost:8000/anforderungsformular/
Django Version: 3.0.8
Exception Type: NameError
Exception Value:
name 'start_tag' is not defined
Exception Location: C:\Program Files (x86)\Python38-32\lib\site-packages\wagtail\contrib\forms\models.py in send_mail, line 293
Python Executable: C:\Program Files (x86)\Python38-32\python.exe
Python Version: 3.8.3
Python Path:
['U:\\IT\\itseite_design',
'C:\\Program Files (x86)\\Python38-32\\python38.zip',
'C:\\Program Files (x86)\\Python38-32\\DLLs',
'C:\\Program Files (x86)\\Python38-32\\lib',
'C:\\Program Files (x86)\\Python38-32',
'C:\\Users\\priwitzl\\AppData\\Roaming\\Python\\Python38\\site-packages',
'C:\\Program Files (x86)\\Python38-32\\lib\\site-packages']
Server time: Di, 11 Aug 2020 12:32:38 +0000
I changed wagtail/contrib/forms/models.py (Basically I added start_tag and end_tag as charFields and added them to the panel.
import json
import os
from django.core.serializers.json import DjangoJSONEncoder
from django.db import models
from django.template.response import TemplateResponse
from django.utils.text import slugify
from django.utils.translation import gettext_lazy as _
from unidecode import unidecode
from wagtail.admin.edit_handlers import FieldPanel
from wagtail.admin.mail import send_mail
from wagtail.core.models import Orderable, Page
from .forms import FormBuilder, WagtailAdminFormPageForm
FORM_FIELD_CHOICES = (
('singleline', _('Single line text')),
('multiline', _('Multi-line text')),
('email', _('Email')),
('number', _('Number')),
('url', _('URL')),
('checkbox', _('Checkbox')),
('checkboxes', _('Checkboxes')),
('dropdown', _('Drop down')),
('multiselect', _('Multiple select')),
('radio', _('Radio buttons')),
('date', _('Date')),
('datetime', _('Date/time')),
('hidden', _('Hidden field')),
)
class AbstractFormSubmission(models.Model):
"""
Data for a form submission.
You can create custom submission model based on this abstract model.
For example, if you need to save additional data or a reference to a user.
"""
form_data = models.TextField()
page = models.ForeignKey(Page, on_delete=models.CASCADE)
submit_time = models.DateTimeField(verbose_name=_('submit time'), auto_now_add=True)
def get_data(self):
"""
Returns dict with form data.
You can override this method to add additional data.
"""
form_data = json.loads(self.form_data)
form_data.update({
'submit_time': self.submit_time,
})
return form_data
def __str__(self):
return self.form_data
class Meta:
abstract = True
verbose_name = _('form submission')
verbose_name_plural = _('form submissions')
class FormSubmission(AbstractFormSubmission):
"""Data for a Form submission."""
class AbstractFormField(Orderable):
"""
Database Fields required for building a Django Form field.
"""
label = models.CharField(
verbose_name=_('label'),
max_length=255,
help_text=_('The label of the form field')
)
field_type = models.CharField(verbose_name=_('field type'), max_length=16, choices=FORM_FIELD_CHOICES)
required = models.BooleanField(verbose_name=_('required'), default=True)
choices = models.TextField(
verbose_name=_('choices'),
blank=True,
help_text=_('Comma separated list of choices. Only applicable in checkboxes, radio and dropdown.')
)
default_value = models.CharField(
verbose_name=_('default value'),
max_length=255,
blank=True,
help_text=_('Default value. Comma separated values supported for checkboxes.')
)
help_text = models.CharField(verbose_name=_('help text'), max_length=255, blank=True)
start_tag = models.CharField(verbose_name=_('start_tag'), max_length=255, blank=True)
end_tag = models.CharField(verbose_name =_('end_tag'), max_length=255, blank=True)
#property
def clean_name(self):
# unidecode will return an ascii string while slugify wants a
# unicode string on the other hand, slugify returns a safe-string
# which will be converted to a normal str
return str(slugify(str(unidecode(self.label))))
panels = [
FieldPanel('label'),
FieldPanel('help_text'),
FieldPanel('required'),
FieldPanel('field_type', classname="formbuilder-type"),
FieldPanel('choices', classname="formbuilder-choices"),
FieldPanel('default_value', classname="formbuilder-default"),
FieldPanel('start_tag'),
FieldPanel('end_tag'),
]
class Meta:
abstract = True
ordering = ['sort_order']
class AbstractForm(Page):
"""
A Form Page. Pages implementing a form should inherit from it
"""
base_form_class = WagtailAdminFormPageForm
form_builder = FormBuilder
submissions_list_view_class = None
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
if not hasattr(self, 'landing_page_template'):
name, ext = os.path.splitext(self.template)
self.landing_page_template = name + '_landing' + ext
class Meta:
abstract = True
def get_form_fields(self):
"""
Form page expects `form_fields` to be declared.
If you want to change backwards relation name,
you need to override this method.
"""
return self.form_fields.all()
def get_data_fields(self):
"""
Returns a list of tuples with (field_name, field_label).
"""
data_fields = [
('submit_time', _('Submission date')),
]
data_fields += [
(field.clean_name, field.label)
for field in self.get_form_fields()
]
return data_fields
def get_form_class(self):
fb = self.form_builder(self.get_form_fields())
return fb.get_form_class()
def get_form_parameters(self):
return {}
def get_form(self, *args, **kwargs):
form_class = self.get_form_class()
form_params = self.get_form_parameters()
form_params.update(kwargs)
return form_class(*args, **form_params)
def get_landing_page_template(self, request, *args, **kwargs):
return self.landing_page_template
def get_submission_class(self):
"""
Returns submission class.
You can override this method to provide custom submission class.
Your class must be inherited from AbstractFormSubmission.
"""
return FormSubmission
def get_submissions_list_view_class(self):
from .views import SubmissionsListView
return self.submissions_list_view_class or SubmissionsListView
def process_form_submission(self, form):
"""
Accepts form instance with submitted data, user and page.
Creates submission instance.
You can override this method if you want to have custom creation logic.
For example, if you want to save reference to a user.
"""
return self.get_submission_class().objects.create(
form_data=json.dumps(form.cleaned_data, cls=DjangoJSONEncoder),
page=self,
)
def render_landing_page(self, request, form_submission=None, *args, **kwargs):
"""
Renders the landing page.
You can override this method to return a different HttpResponse as
landing page. E.g. you could return a redirect to a separate page.
"""
context = self.get_context(request)
context['form_submission'] = form_submission
return TemplateResponse(
request,
self.get_landing_page_template(request),
context
)
def serve_submissions_list_view(self, request, *args, **kwargs):
"""
Returns list submissions view for admin.
`list_submissions_view_class` can bse set to provide custom view class.
Your class must be inherited from SubmissionsListView.
"""
view = self.get_submissions_list_view_class().as_view()
return view(request, form_page=self, *args, **kwargs)
def serve(self, request, *args, **kwargs):
if request.method == 'POST':
form = self.get_form(request.POST, request.FILES, page=self, user=request.user)
if form.is_valid():
form_submission = self.process_form_submission(form)
return self.render_landing_page(request, form_submission, *args, **kwargs)
else:
form = self.get_form(page=self, user=request.user)
context = self.get_context(request)
context['form'] = form
return TemplateResponse(
request,
self.get_template(request),
context
)
preview_modes = [
('form', _('Form')),
('landing', _('Landing page')),
]
def serve_preview(self, request, mode):
if mode == 'landing':
request.is_preview = True
return self.render_landing_page(request)
else:
return super().serve_preview(request, mode)
class AbstractEmailForm(AbstractForm):
"""
A Form Page that sends email. Pages implementing a form to be send to an email should inherit from it
"""
to_address = models.CharField(
verbose_name=_('to address'), max_length=255, blank=True,
help_text=_("Optional - form submissions will be emailed to these addresses. Separate multiple addresses by comma.")
)
from_address = models.CharField(verbose_name=_('from address'), max_length=255, blank=True)
subject = models.CharField(verbose_name=_('subject'), max_length=255, blank=True)
def process_form_submission(self, form):
submission = super().process_form_submission(form)
if self.to_address:
self.send_mail(form)
return submission
def send_mail(self, form):
addresses = [x.strip() for x in self.to_address.split(',')]
content = []
for field in form:
value = field.value()
if isinstance(value, list):
value = ', '.join(value)
content.append('{}: {}'.format(start_tag, field.label, value, end_tag))
content = '\n'.join(content)
send_mail(self.subject, content, addresses, self.from_address,)
class Meta:
abstract = True
I guess the problem occurs when trying to send the mail.
The form-website loads and I can fill in any fields. But when I try to submit I get the error.
content.append('{}: {}'.format(start_tag, field.label, value, end_tag))
My guess is that I call the variables in a wrong way, but I can't figure how to do it correctly.
I hope I explained my problem well. I'm happy about any help I can get.
As I see..
the first issue is in this
content.append('{}: {}'.format(start_tag, field.label, value, end_tag))
got 4 arguments while you provided only two Curly brackets only,plus, you didn't import the ((start_tag)) nor ((end_tag)), because you defined them in a different model (as fields) but you called them in the different model that you combined them with field.label and the value,
hint: if still you get an error,you should probably check tyopos (writing mistakes)
for this
(and so on. Now comes the tricky part I guess: I want to use the "start_tag" and "end_tag" when generating the email. So instead of
this: "label": "value" linebreak "label": "value"..., I want to do
this: "start_tag""label": "value""end_tag" linebreak
"start_tag""label": "value""end_tag"... Since my problem occured when
the system tries to send the email, I know that I call the variables
"start/end_tag" in a wrong way. As u said, I try to call a variable of
a different module. I am confused since the variable "field.label" can
be called, but not "field.start_tag",etc. I hope this helps)
the best way (my opinion) is to create your decorator and wrap your latest needed dict into it.
For your info: the Emailing process has nothing to do with what you need..!
I am trying to make import-export select current/save user(defined as "author" field in model)
I tried this(save_model) but it doesn't work(I think because of resource class)
class KWResource(resources.ModelResource):
class Meta:
model = KW
import_id_fields = ('Keyword',)
fields = ('Keyword',)
searchess = fields.Field(attribute='searches', column_name="Avg. Monthly Searches (exact match only)")
compp = fields.Field(attribute='comp', column_name="Competition")
cpcc = fields.Field(attribute='cpc', column_name="Suggested bid")
def save_model(self, request, obj, form, change):
if getattr(obj, 'author', None) is None:
obj.author = request.user
obj.save()
How to get id of admin user that initiated import here?
Figured it out, not sure if it's the best solution but it works:
class KWResource(resources.ModelResource):
....
....
def before_import(self, dataset, dry_run, *args, **kwargs):
li = []
li.append(kwargs.pop('user', 1))
dataset.insert_col(0, li, 'user')
return super(KWResource, self).before_import(dataset, dry_run, *args, **kwargs)
For anyone finding this later, you can access the current django user in a hook on your Resource. There's a 'user' in the **kwargs.
def before_import_row(self, row, **kwargs):
row['author'] = kwargs['user'].id
More detail here:
django-import-export assign current user
I have a form that I need to show my project outside the area of administration, some fields can not be edited but can see them.
To do this would be great "AdminReadonlyField" found in "django.contrib.admin.helpers" The problem is that you can not do.
I have tried to create some widget that can replace this complex class, but I can not get it to work properly with DateTiemField fields.
class UserUpdateForm(forms.ModelForm):
"""
We need field "date_joined" can not be edited but can see them
"""
class Meta:
model = User
fields = ('first_name', 'last_name',
'email', 'date_joined', 'slug')
def __init__(self, user, *args, **kwargs):
kwargs['instance'] = user
super(UserUpdateForm, self).__init__(*args, **kwargs)
self.fields['date_joined'].widget = widgets.CMDateTimeText()
def clean_date_joined(self):
instance = getattr(self, 'instance', None)
if instance and instance.pk:
return instance.date_joined
else:
return self.cleaned_data['date_joined']
My code, something is not right.
class CMDateTimeText(DateTimeBaseInput):
"""
A SplitDateTime Widget that has some admin-specific styling.
Hereda Field and Widget.
"""
format_key = 'DATETIME_FORMAT'
def __init__(self, attrs=None, format=None):
# Use slightly better defaults than HTML's 20x2 box
default_attrs = {'class': 'date_id'}
if attrs:
default_attrs.update(attrs)
super(CMDateTimeText, self).__init__(attrs=default_attrs, format=format)
def render(self, name, value, attrs=None):
if value is None:
value = ''
value = self._format_value(value)
final_attrs = self.build_attrs(attrs, name=name)
return format_html('<p{}>{}</p>', flatatt(final_attrs), conditional_escape(value))
Result image:
any idea how to do "AdminReadonlyField"" any view or form?
So after hours of looking for various solutions, I found out how to do it the Django way.
Simply add the attribute disabled to the field in the form (not the widget!):
# in __init__() with crispy-forms for instance
self.fields['field'].disabled = True
# as form field
field = forms.CharField(disabled=True)
And it works... Django is taking care of not saving the field, if some hacker tampered with it although it's disabled. Only works with Django 1.9+.
From this question I want to convert my form from regular Form to ModelForm so I can take advantage of instance parameter in ModelForm.
Here is my current form code:
class OrderDetailForm(forms.Form):
def __init__(
self,
user,
can_edit_work_type=None,
can_edit_vendor=None,
can_edit_note=None,
*args,
**kwargs
):
super(OrderDetailForm, self).__init__(*args, **kwargs)
if can_edit_work_type:
self.fields['work_type'] = forms.ChoiceField(choices=Order.WORK_TYPE_CHOICES)
if can_edit_vendor:
self.fields['vendor'] = forms.ModelChoiceField(
queryset=Vendor.objects.all(),
empty_label="Choose a vendor",
)
if can_edit_note:
self.fields['note'] = forms.CharField(widget=forms.Textarea)
def clean(self):
super(OrderDetailForm, self).clean()
if 'note' in self.cleaned_data:
if len(self.cleaned_data['note']) < 50:
self._errors['note'] = self.error_class([u"Please enter a longer note."])
del self.cleaned_data['note']
return self.cleaned_data
As you can see, I have some if statements that determine whether the fields even show you in the forms (logically it means certain users can only edit certain parts of the fields).
How would I do that in ModelForm? I understand fields to be a tuple, so it can't be appended like I did in Form. So I want to do something like
class OrderDetailForm(forms.ModelForm):
class Meta:
model = Order
# fields = ('work_type', 'vendor', 'note') I can't do that since I need to be able to control it. See below.
# Can I control widgets even if that field doesn't exist?
widgets = {
'note': forms.Textarea(),
}
def __init__(
self,
user,
can_edit_work_type=None,
can_edit_vendor=None,
can_edit_note=None,
*args,
**kwargs
):
super(OrderDetailForm, self).__init__(*args, **kwargs)
fields = []
if can_edit_work_type:
fields.append('work_type')
if can_edit_vendor:
fields.append('vendor')
if can_edit_note:
fields.append('note')
self.Meta.fields = tuple(fields) # Does this work?
def clean(self):
super(OrderDetailForm, self).clean()
if 'note' in self.cleaned_data:
if len(self.cleaned_data['note']) < 50:
self._errors['note'] = self.error_class([u"Please enter a longer note."])
del self.cleaned_data['note']
return self.cleaned_data
Is that possible? How do you control the fields in ModelForm?
Another possible way is generate a inline form class in the view to exclude fields based on the request, for example, define a normal model form for Order model, called OrderDetailForm:
class OrderDetailForm(forms.ModelForm):
class Meta:
model = Order
fields = ('work_type', 'vendor', 'note')
widgets = {
'note': forms.Textarea(),
}
In the view, for example, edit order, create a customized form based on the OrderDetailForm:
def edit(request, order_id):
order = Order.objects.get(pk=order_id)
can_edit_work_type = bool(request.REQUEST.get('can_edit_work_type', False))
can_edit_vender = bool(request.REQUEST.get('can_edit_vender', False))
can_edit_note = bool(request.REQUEST.get('can_edit_note', False))
exclude_fields = []
if not can_edit_work_type:
exclude_fields.append('work_type')
if not can_edit_vender:
exclude_fields.append('vender')
if not can_edit_note:
exclude_fields.append('note')
class CustomizedOrderForm(OrderDetailForm):
class Meta:
model = Order
exclude = tuple(exclude_fields)
if request.method == 'POST':
form = CustomizedOrderForm(instance=order, data=request.POST)
if form.is_valid():
form.save()
else:
form = CustomizedOrderForm(instance=order)
return render(request, 'order_form.html', {'form': form})
The ModelForm api is very similar to that of the regular Form. The advantage is that you now get model validation in addition to conveniences like default widgets, the instance kwarg, and the save method.
fields attr is still dict-like. You can see fields getting built by the metaclass here. Then, going through the inheritance and calling super() in the BaseModelForm.__init__, we arrive at a deepcopy of the declared fields, originally a SortedDict. This is common to Form and ModelForm, both subclasses of BaseForm.
Put the fields in the exclude and add them the way you are doing in the original __init__.
Clean them the same way.
Then, you can override the save method: you can call super() to get the object back and deal with the data in cleaned_data however you want.
class OrderDetailForm(forms.ModelForm):
# regular fields, not based on bools
# ...
class Meta:
model = Order
exclude = ('work_type', 'vendor', 'note')
# or fields = (...other fields )
def __init__(
self,
user,
can_edit_work_type=None,
can_edit_vendor=None,
can_edit_note=None,
*args,
**kwargs,
):
super(OrderDetailForm, self).__init__(*args, **kwargs)
if can_edit_work_type:
self.fields['work_type'] = forms.ChoiceField(
choices=Order.WORK_TYPE_CHOICES)
if can_edit_vendor:
self.fields['vendor'] = forms.ModelChoiceField(
queryset=Vendor.objects.all(),
empty_label="Choose a vendor",
)
if can_edit_note:
self.fields['note'] = forms.CharField(widget=forms.Textarea)
def clean(self):
# I never call super() in clean .. do I? .. hmmm
# maybe I should or is sth magic going on?
# alternately,
# data = self.cleaned_data
# let's call super though
data = super(OrderDetailForm, self).clean()
if 'note' in data:
if len(data['note']) < 50:
# I raise a validation error so .is_valid() comes back False
# form.errors happens magically ...
raise forms.ValidationError("Not long enough ...")
return data
def save(self, *args, **kwargs):
data = self.cleaned_data
# maybe do some stuff here
# ...
# commit=True or commit=False could be important
order = super(OrderDetailForm, self).save(*args, **kwargs)
if 'note' in data:
order.note = data['note']
# ... do other stuff
# probably ...
order.save()
# respect how model forms work.
return order