Django forms how to add form field attribute - python

I'm using django-crispy-forms I want to add an attribute http-prefix to the outputted domain field, for example like this...
<input type="text" name="domain" http-prefix>
How is this possible? I can see crispy-forms has the ability to add css to a field self.helper.field_class, but I cannot see where to add an attribute to a field like my example above just http-prefix.
My Form:
class SchemeForm(NgModelFormMixin, forms.ModelForm):
def __init__(self, *args, **kwargs):
super(SchemeForm, self).__init__(*args, **kwargs)
self.helper = FormHelper()
self.helper.form_class = 'form-horizontal'
self.helper.label_class = 'col-lg-3'
self.helper.field_class = 'col-lg-8'
self.helper.layout = Layout(
'name',
'domain',
'slug',
class Meta:
model = Scheme
fields = ('name', 'domain', 'slug')

Simply update the attribute by setting the value to empty string:
def __init__(self, *args, **kwargs):
super(SchemeForm, self).__init__(*args, **kwargs)
#...
self.fields['domain'].widget.attrs['http-prefix'] = ''

Related

How to use add_form for Django admin?

How can i correctly use add_form in admin panel when class is inherited from admin.ModelAdmin. I find out the hack with overriding get_form method where you can dynamically change form to add_form value. With current approach i'm getting this error
formsets, inline_instances = self._create_formsets(request, form.instance, change=False)
AttributeError: 'UserForm' object has no attribute 'instance'
form.py
class AddCustomProductForm(forms.Form):
users = forms.ChoiceField(
label='Select a Profile',
required=True,
)
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields["users"].choices = CustomUser.objects.all()
admin.py
class PostAdmin(admin.ModelAdmin):
list_display = ('email', 'created', 'company_id',)
add_form = AddCustomProductForm
form = CustomProductForm
fieldsets = (
(None, {"fields": ("users")}),
)
def get_form(self, request, obj=None, **kwargs):
defaults = {}
if obj is None:
defaults['form'] = self.add_form
defaults.update(kwargs)
return super().get_form(request, obj, **defaults)
You need a ModelForm, where you take the values directly from a Model( in this case ModelAdmin) in a form.
https://docs.djangoproject.com/en/4.0/topics/forms/modelforms/
The other way is make "your" admin panel.

Validate django form with a list of values from multiple CheckboxSelectMultiple

Using Django2.0, I have set up a form which displays all of my model objects in a list with check boxes. When this is submitted, in my request dict it stores them as a list of IDs. How do i validate these within the form? The clean() method does not get called.
forms:
class SampleRunSearchForm(forms.ModelForm):
sample_run_id = forms.ModelChoiceField(
label='Sample Run',
queryset=SampleRun.objects.all(),
widget=forms.CheckboxSelectMultiple,
)
class Meta:
model = SampleRun
fields = ('sample_run_id',)
def __init__(self, *args, **kwargs):
super(SampleRunSearchForm, self).__init__(*args, **kwargs)
self.helper = FormHelper()
self.helper.layout = Layout(
Field('sample_run_id', css_class='sample-run-display',),
HTML('<br>'),
Submit('submit', 'Report samples', css_class='upload-btn')
)
self.helper.form_method = 'GET'
def clean_sample_run_id(self):
sr_id = self.cleaned_data.getlist('sample_run_id')
for sr in sr_id:
... do something to validate...
else:
raise(forms.ValidationError('Error'))
return(sr_id)
views:
class SearchSampleRun(View):
samplerunform = SampleRunSearchForm
template_name = 'results/samplerun_search_form.html'
def get(self, request, *args, **kwargs):
samplerunform = self.samplerunform()
if request.GET:
samplerunform = self.samplerunform(request.GET)
samplerunform.is_valid()
context = {'samplerunform': samplerunform}
return render(request, self.template_name, context)
It returns an error to my page that is it not a valid choice.
the clean() method will also execute, but clean_sample_run_id() doesn't work - do i need to iterate through each ID and pass it through the form seperately for validation?!

Filter Django ModelForm without validating it

I have a form which I would like to filter based on information passed by another form, but without validating it just yet:
forms.py:
class SampleRunSearchForm(forms.ModelForm):
class Meta:
model = SampleRun
fields = ('id',)
def __init__(self, sr_obj, *args, **kwargs):
super(SampleRunSearchForm, self).__init__(*args, **kwargs)
self.fields['id'] = forms.ChoiceField(required=True,
label='Sample:',
widget=forms.CheckboxSelectMultiple,
choices=((s.id, s) for s in sr_obj)
)
self.helper = FormHelper()
self.helper.layout = Layout(
Field('id', css_class='sample-run-display',),
Submit('submit', 'Report samples', css_class='upload-btn')
)
self.helper.form_method = 'POST'
views.py:
class SearchSampleRun(View):
samplerunform = SampleRunSearchForm
template_name = 'results/samplerun_search_form.html'
def get(self, request, *args, **kwargs):
self.run_obj = get_object_or_404(Run, id=kwargs['run_id'])
self.choice = kwargs['choice']
self.sample_run_obj = self.obtainCorrectSamples()
samplerunform = self.samplerunform(sr_obj=self.sample_run_obj)
context = {'samplerunform': samplerunform}
return render(request, self.template_name, context)
def post(self, request, *args, **kwargs):
samplerunform = self.samplerunform(request.POST)
if samplerunform.is_valid():
HttpResponseRedirect(...somewhere to display information)
context = {}
return render(request, self.template_name, context)
The initial form (not shown) takes a charfield and redirects to my SearchSampleRun view with **kwargs. I want to filter my SampleRunSearchForm based on these kwargs and display a list of check boxes - filtered model object from the SampleRun model. This works, but when i click these buttons, and submit the form, it initialised again, and sr_obj is None, so the form field produces an error.
I have tried using:
sr_obj = kwargs.pop('sr_obj', None)
In my init() method, but these must be a way to dynamically filter a form queryset in order to display a subset of values, before validating, with a view to validating when this form is submitted?
Just add validation to the __init__ method and override id fields only if sr_objis not empty:
def __init__(self, sr_obj, *args, **kwargs):
super(SampleRunSearchForm, self).__init__(*args, **kwargs)
if sr_obj:
self.fields['id'] = forms.ChoiceField(required=True,
label='Sample:',
widget=forms.CheckboxSelectMultiple,
choices=((s.id, s) for s in sr_obj)
)
self.helper = FormHelper()
self.helper.layout = Layout(
Field('id', css_class='sample-run-display',),
Submit('submit', 'Report samples', css_class='upload-btn')
)
self.helper.form_method = 'POST'

How to remove fields from CreateView depending on the user in Django?

I created a CBV of which I want to remove one or more fields, depending on the user. The idea is a jobsite and if the logged in user is a recruiter, than the employer field should be included, otherwise it should be excluded.
forms.py
class JobCreationForm(forms.ModelForm):
class Meta:
model = Job
# exclude = ['posted', 'provider', 'ext_id']
fields = ('title',
'job_desc',
'agency_name',
'employer',
'contact_name',
)
views.py
class JobCreateView(LoginRequiredMixin, CreateView):
template_name = 'job/job.html'
form_class = JobCreationForm
success_url = '/'
def get_context_data(self, **kwargs):
context = super(JobCreateView, self).get_context_data(**kwargs)
# import the Customers of this Company
self.fields["agency_name"].remove()
recruiter = self.request.user
self.fields["contact_name"].queryset = Profile.objects.filter(user_id = self.request.user)
# if the user is a recruiter, delete the employer field.
if Company.objects.filter(user_id = self.request.user).values('is_recruiter') == False:
pass
# self.fields.remove("employer")
del self.fields["employer"]
return context
The current error is NoneType' object has no attribute '__getitem__'.
My question: how can I remove a field from the form based on logic? I tried these versions:
self.fields["employer"].delete()
self.fields.remove("employer")
del self.fields["employer"]
Any tips?
The correct way to implement this (modify the fields of the form depending on user) is to do it on your form's __init__ method. However in order for the form to access the current user you need to pass the user to it from your view. To do this you'll use the get_form_kwargs method. Thus, start by adding the following method to your view:
def get_form_kwargs(self):
kwargs = super().get_form_kwargs()
kwargs.update({'user': self.request.user})
return kwargs
And now, you can add an __init__ to your form like this:
def __init__(self, *args, **kwargs):
self.user = kwargs.pop('user', None)
super().__init__(*args, **kwargs)
if Company.objects.filter(user_id = self.user).is_recruiter == False:
self.fields.pop("employer")
self.fields.pop('owned_by')
Notice that you first initialize the form (using super.__init__) and then you can modify the fields to your heart's content.
There are few ways to go about it.
I find having 2 separate forms RecruiterEmployeeForm and EmployeeForm may be neater.
class RecruiterEmployeeForm(forms.ModelForm):
model = Job
fields = ('title',
'job_desc',
'agency_name',
'employer',
'contact_name',
)
class EmployeeForm(forms.ModelForm):
model = Job
fields = ('title',
'job_desc',
'agency_name',
'contact_name',
)
Then you can override ger_form_class for the CBV
def get_form_class(self):
if self.request.user.is_recruiter():
return RecruiterEmployeeForm
else:
return EmployeeForm
To send extra kwargs to use generic view method get_form_kwargs and to get extra kwargs override __init__ of form and pop the extra kwargs.
forms.py
class JobCreationForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
user = kwargs.pop('user', None)
super(JobCreationForm, self).__init__(*args, **kwargs)
if Company.objects.filter(user_id = self.user).is_recruiter == False:
self.fields.pop("employer")
class Meta:
model = Job
# exclude = ['posted', 'provider', 'ext_id']
fields = ('title', 'job_desc', 'agency_name', 'employer', 'contact_name')
views.py
class JobCreateView(LoginRequiredMixin, CreateView):
template_name = 'job/job.html'
form_class = JobCreationForm
success_url = '/'
def get_form_kwargs(self):
kwargs = super(JobCreateView, self).get_form_kwargs()
kwargs.update({'user': self.request.user})
return kwargs

Django Crispy form set model field as required

I have a modelform in which I want to set required attribute to True for email validation
field:-email
class RegisterMyBuisinessForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
self.helper = FormHelper()
self.helper.form_method = 'post'
self.helper.form_action = '/registermybuisness/'
Field('email', type='email')
self.helper.add_input(Submit('submit', 'Submit',css_class="btn c-theme-btn c-btn-square c-btn-bold c-btn-uppercase"))
super(RegisterMyBuisinessForm, self).__init__(*args, **kwargs)
class Meta:
model = RegistermyBusiness
fields = ['name','email', 'org_name', 'contact','business_description','store_address','message','store_landmark','business_type','city']
I tried
self.fields['email'].required=True
this resulted in class RegisterMyBuisinessForm doesnot have fields error
You can alter self.fields['email'] in the __init__ method. You need to call super() first.
class RegisterMyBuisinessForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
...
super(RegisterMyBuisinessForm, self).__init__(*args, **kwargs)
self.fields['email'].required = True
...

Categories