I have a template in which I edit all the basic fields of a model User.
Now I want to have a form in which there will be displayed all available permissions in a checkbox-like style. The ones already assigned to the User should already be checked.
I have a form.py, with a form for editing User, but have not completed the permissions form.
class UserSettingsForm(forms.ModelForm):
class Meta:
model = User
fields = (
'username',
'first_name',
'last_name',
'email',
'urnik',
'ure_na_teden',
'rfid',
'oddelek',
'status_zaposleni',
)
def __init__(self, *args, **kwargs):
self.request = kwargs.pop("request")
super(UserSettingsForm, self).__init__(*args, **kwargs)
instance = getattr(self, 'instance', None)
self.fields['username'].disabled = True
def save(self):
user = super(UserSettingsForm, self).save()
return user
class UserPermissonsForm(forms.ModelForm):
class Meta:
model = Permission
fields = (
'name',
'content_type',
'codename',
)
And a views.py:
#login_required
def uporabnik_uredi(request, username=None):
user = get_object_or_404(User, username=username)
uporabnik_form = UserSettingsForm(request.POST or None,request=request, instance=user)
permissions = [(p.id, p.name) for p in Permission.objects.filter(user=user)]
data = {
'uporabnik' : user,
'form' : uporabnik_form,
'from': request.GET.get('from', None),
'permissions' : permissions,
}
if request.POST:
if uporabnik_form.is_valid():
user = uporabnik_form.save()
next = request.GET.get('next', None)
return redirect(next)
return render(request, "sifranti/uporabniki/uredi.html", data)
And also a template, just to see which current permissions are assigned to the user:
<div class="">
{% for id, name in permissions %}
{{ id }} - {{ name }} <br>
{% endfor %}
</div>
This is how I do but i use a MultipleSelect, I guess you can apply some of this to checkboxes.
class EditUserForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(EditUserForm, self).__init__(*args, **kwargs)
def get_label(obj):
permission_name = str(obj).split('|')[2].strip()
model_name = permission_name.split(' ')[2].strip()
return '%s | %s' % (model_name.title(), permission_name)
User = get_user_model()
content_type = ContentType.objects.get_for_model(User)
self.fields['user_permissions'].queryset = Permission.objects.filter(content_type=content_type)
self.fields['user_permissions'].widget.attrs.update({'class': 'permission-select'})
self.fields['user_permissions'].help_text = None
self.fields['user_permissions'].label = "Label"
self.fields['user_permissions'].label_from_instance = get_label
def save(self, commit=True):
user_instance = super(EditUserForm, self).save(commit)
user_instance.save()
user_instance.user_permissions.set(self.cleaned_data.get('user_permissions'))
return user_instance
class Meta:
model = get_user_model()
fields = ['email', 'first_name', 'last_name', 'user_permissions']
widgets = {
'email': forms.EmailInput(attrs={'class': 'form-control', 'style': 'width: 300px;'}),
'first_name': forms.TextInput(attrs={'class': 'form-control', 'style': 'width: 300px;'}),
'last_name': forms.TextInput(attrs={'class': 'form-control', 'style': 'width: 300px;'}),
'user_permissions': forms.SelectMultiple(attrs={'style': 'width: 350px; height: 200px;'})
}
Hope this helps, good luck!
Related
I have normally just a simple question but I can't get it working.
I have a view where customers can add, delete or edit their addresses.
so the forms are generating, and I can open the edit_customer_form, the values are filled in because query set. But if I click on save, the form is posting, its hopping in the right loop: if 'edit_address' in request.POST: but I got the output from: print('ERROR', edit_address_form.errors)
that all fields are required and there are no inputs given. That is strange.
The HTML forms looks like:
<input type="text" name="form-0-Address_Firstname" value="Christopher" class="form-control" autofocus="" id="id_form-0-Address_Firstname">
The error says:
ERROR [{'CustomerID': ['Dette felt er påkrævet.'], 'ID': ['Dette felt er påkrævet.']}, {'Address_Firstname': ['Dette felt er påkrævet.'],
In my eyes the field name is different to the value given by the prefix, which is necessary. But how can I save the form and give the values to the form.
view.py:
customer_addresses = CustomerAddresses.objects.filter(CustomerID=customer)
AddressFormSet = modelformset_factory(CustomerAddresses, form=CustomerAddressesForm, extra=0)
formset = AddressFormSet(queryset=customer_addresses, form_kwargs={'user': User.ID})
if request.method == 'POST':
if 'add_address' in request.POST:
add_address_form = CustomerAddressesForm(User.ID, request.POST)
if add_address_form.is_valid():
add_address_form.save()
if 'edit_address' in request.POST:
address_id = request.POST.get('id_Address')
address_data = CustomerAddresses.objects.get(pk=address_id)
edit_address_form = AddressFormSet(request.POST, queryset=customer_addresses, form_kwargs={'user': User.ID})
print('ERROR', edit_address_form.errors)
messages.error(request, 'ERROR')
if edit_address_form.is_valid():
instances = edit_address_form.save(commit=False)
for instance in instances:
instance.save()
return redirect('addresses')
if 'delete_customer_address_id' in request.POST:
delete_customer_address_id = request.POST.get('delete_customer_address_id')
request.session['delete_customer_address_id'] = delete_customer_address_id
return redirect('delete-customer-address')
if 'register_customer' in request.POST:
register_form = CustomerAddressesForm(user=user_id)
if register_form.is_valid():
customer = register_form.save(commit=False)
customer.UserID = user_id
customer.save()
# You can redirect to a success page or login the user directly
redirect(request.META.get('HTTP_REFERER'))
else:
add_address_form = CustomerAddressesForm(user=User.ID)
and my form:
class CustomerAddressesForm(forms.ModelForm):
Title = forms.CharField(widget=forms.TextInput(attrs={'class': 'form-control mb-3', 'autofocus': True}),
required=False)
Address_Firstname = forms.CharField(widget=forms.TextInput(attrs={'class': 'form-control', 'autofocus': True}))
Address_Lastname = forms.CharField(widget=forms.TextInput(attrs={'class': 'form-control', 'autofocus': True}))
Zip = forms.IntegerField(
widget=forms.TextInput(attrs={'class': 'form-control', 'maxlength': '5', 'data-toggle': 'maxlength'}),
label='Postnummer')
City = forms.CharField(widget=forms.TextInput(attrs={'class': 'form-control', 'autofocus': True}))
Address_Company = forms.CharField(widget=forms.TextInput(attrs={'class': 'form-control', 'autofocus': True}),
required=False)
Street = forms.CharField(widget=forms.TextInput(attrs={'class': 'form-control', 'autofocus': True}), required=False)
Country = forms.ModelChoiceField(widget=forms.Select(attrs={'class': 'form-select'}),
queryset=CountryList.objects.values_list('countryname', flat=True).order_by(
'code'), initial='Denmark', to_field_name='countryname')
Is_Default_Shipping_address = forms.BooleanField(
widget=forms.CheckboxInput(attrs={'class': 'form-check-input', 'id': 'customSwitch4'}), initial=False,
required=False)
Is_Default_Billing_address = forms.BooleanField(
widget=forms.CheckboxInput(attrs={'class': 'form-check-input', 'id': 'customSwitch3'}), initial=False,
required=False)
class Meta:
model = CustomerAddresses
fields = ['Title', 'Address_Firstname', 'Address_Lastname', 'Zip', 'City', 'Address_Company', 'Country',
'Is_Default_Shipping_address', 'Is_Default_Billing_address', 'Address_CustomerSalutation',
'CustomerID', 'Street']
def __init__(self, user, *args, **kwargs):
super(CustomerAddressesForm, self).__init__(*args, **kwargs)
self.fields['Address_CustomerSalutation'] = forms.ModelChoiceField(
widget=forms.Select(attrs={'class': 'form-select mb-3'}),
queryset=Salutation.objects.filter(UserID_id=user), empty_label=None)
I found the the problem, it was a misunderstand, I created for every instance a own <form> tag, this was not working because
{{ formset.management_form }}
have to be in the form for all formsets.
I tried also
{{ form.instance.management_form }}
and
{{ form.management_form }}
in the {% for form in formset %} loop, but it was not working. I also excluded the CustomerID field because it will not be changed by the customer or the admin.
in my form i want to select department from 2 options: some object(every time only one) and None.
my form.py
class TeamGoalForm(ModelForm):
def __init__(self, *args, **kwargs):
employees = kwargs.pop('employees')
department = kwargs.pop('department')
super().__init__(*args, **kwargs)
self.fields['employees'].queryset = employees
self.fields['department'].choices = [(1, department), (2, None)]
self.fields['department'].initial = [1]
class Meta:
model = TeamGoal
fields = ('team_goal_title','department','employees', 'team_goal_description', 'gpd_year','team_factor_0','team_factor_1','team_factor_2','team_factor_3','team_factor_weight')
widgets = {
'team_goal_title': forms.TextInput (attrs={'class':'form-control', 'placeholder':'Enter the title of goal'}),
'department': forms.Select (attrs={'class': 'form-control', 'placeholder':'Select department'}), }
in my view.py I have had:
if request.method == 'POST':
form = TeamGoalForm(request.POST, employees=employees, department=department)
if form.is_valid():
form.save()
Here my department is an object.
How to implement something like this, 'cos my solution does't work?
You are not showing much of the code, but here is more or less how the different files should look like, hope this helps:
**#models.py**
from django.db import models
class department(models.Model):
# department fields
pass
class TeamGoal(models.Models):
...
deparment = models.ForeignKey(department, on_delete=models.CASCADE)
...
**#forms.py**
from django.forms import ModelForm
from django import forms
class TeamGoalForm(ModelForm):
class Meta:
model = TeamGoal
fields = (
'team_goal_title',
'department',
'employees',
'team_goal_description',
'gpd_year',
'team_factor_0',
'team_factor_1',
'team_factor_2',
'team_factor_3',
'team_factor_weight'
)
deparments = deparment.objects.all()
widgets = {
'deparments': forms.Select(choices=deparments,
attrs={
'class': 'form-control',
'placeholder':'Select department'
})
**#views.py**
from .forms import TeamGoalForm
def AddTeamGoalForm(request):
context = {
'TeamGoalForm': TeamGoalForm,
}
if request.method == 'POST':
addTeamGoalForm = TeamGoalForm(request.POST)
if addTeamGoalForm.is_valid():
newTeamGoalForm = addTeamGoalForm.save()
return redirect('/')
else:
return render(request, '/TeamGoalForm.html', context)
**# TeamGoalForm.html**
<form method="POST">
{% csrf_token %}
{{ TeamGoalForm}}
<button type="submit" class="btn btn-primary">Submit</button>
</form>
NOTE: you may need to adjust it based on the code you've already writen, but hopefully this leads close to a solution.
I get an error when I try to add a widget to the form.
The error:
File "C:\Users\lib\site-packages\django\forms\fields.py", line 558, in __init__
super().__init__(**kwargs)
TypeError: __init__() got an unexpected keyword argument 'attrs'
The model
class Video(models.Model):
author = models.ForeignKey(Account, on_delete=models.CASCADE)
video = models.FileField(upload_to='post-videos')
title = models.CharField(max_length=100)
description = models.TextField(null=True, blank=True)
video_poster = models.ImageField(max_length=255, upload_to='post-videos')
The views
def VideosUploadView(request, *args, **kwargs):
all_videos = Video.objects.all()
V_form = Video_form()
video_added = False
if not request.user.is_active:
# any error you want
return redirect('login')
try:
account = Account.objects.get(username=request.user.username)
except:
# any error you want
return HttpResponse('User does not exits.')
if 'submit_v_form' in request.POST:
print(request.POST)
V_form = Video_form(request.POST, request.FILES)
if V_form.is_valid():
instance = V_form.save(commit=False)
instance.author = account
instance.save()
V_form = Video_form()
video_added = True
contex = {
'all_videos': all_videos,
'account': account,
'V_form': V_form,
'video_added': video_added,
}
return render(request, "video/upload_videos.html", contex)
The form
class Video_form(forms.ModelForm):
class Meta:
model = Video
fields = ('title', 'description', 'video', 'video_poster')
widgets = {
'title': forms.TextInput(attrs={'class': 'form-control'}),
'description': forms.TextInput(attrs={'class': 'form-control'}),
'video': forms.FileField(widget=forms.FileInput(attrs={'class': 'form-control'})),
'video_poster': forms.ImageField(attrs={'class': 'form-control'}),
}
You must assign valid widget in the Video_form:
widgets = {
'title': forms.TextInput(attrs={'class': 'form-control'}),
'description': forms.TextInput(attrs={'class': 'form-control'}),
'video': forms.FileInput(attrs={'class': 'form-control'}),
'video_poster': forms.ClearableFileInput(attrs={'class': 'form-control'}),
}
forms.FileField and forms.ImageField are fields not widgets.
My problem was style the form fields and I explored a good way to styling the fields by add the field name after the form name
Like this way
<span class="control-fileupload">
<!--here what I mean--> {{ V_form.video }}
<label for="file1" class="text-left">click to choose a Video on your computer.</label>
</span>
and here also
<label>{{ V_form.title }}</label>
I'm using Django Model Form. Can anyone help me validate those fields to get field error using clean()?
The Name field cannot be repeated in the same office, only in a different one.
form.py
class CreateSalesRepForm(forms.ModelForm):
class Meta:
model = CreateSalesRep
fields = ['name', 'office']
widgets = {
'office': forms.Select(attrs={'class': 'form-control', 'placeholder': 'Enter Office'}),
'name': forms.TextInput(attrs={'class': 'form-control', 'placeholder': 'Enter Name'})
}
UPDATED (form.py) --- Here is the solution, you can use this codes to validate both insert and update form.
def clean(self):
cleaned_data = super().clean()
office = cleaned_data.get("office")
name = cleaned_data.get("name")
duplicates = CreateSalesRep.objects.filter(office=office, name=name)
if (self.instance.pk and None):
duplicates = duplicates.filter(pk=self.instance.pk)
if duplicates.exists():
msg = "Name already exist in office selected"
self.add_error('name', msg)
self.add_error('office', msg)
view.py
def create_salesrep(request):
if request.method == "POST":
form = CreateSalesRepForm(request.POST or None)
if form.is_valid():
form.save()
messages.success(request, 'Successfully Saved!', 'alert-success')
return redirect('sales_rep')
else:
return render(request, 'salesrep/create_salesrep.html', {'form':form})
else:
form = CreateSalesRepForm()
context = {'form':form}
return render(request, 'salesrep/create_salesrep.html', context)
def update_salesrep(request, pk):
srep = CreateSalesRep.objects.get(id=pk)
form = CreateSalesRepForm(instance=srep)
if request.method == "POST":
form = CreateSalesRepForm(request.POST or None, instance=srep)
if form.is_valid():
form.save()
messages.success(request, 'Successfully Updated!', 'alert-success')
return redirect('sales_rep')
else:
return render(request, 'salesrep/update_salesrep.html', {'form':form})
else:
form = CreateSalesRepForm(instance=srep)
return render(request, 'salesrep/update_salesrep.html', {'form':form})
You can validate in the forms:
class CreateSalesRepForm(forms.ModelForm):
class Meta:
model = CreateSalesRep
fields = ['name', 'office']
def clean(self):
cleaned_data = super().clean()
office= cleaned_data.get("office")
name = cleaned_data.get("name")
duplicates = CreateSalesRep.objects.filter(office=office, name=name)
if self.instance.pk:
duplicates = duplicates.filter(pk=self.instance.pk)
if duplicates.exists():
raise forms.ValidationError('Name already in office')
That being said, you could enforce this in the model/db level as well by adding the following to your CreateSalesRep model:
class CreateSalesRep(models.Model):
...
class Meta:
unique_together = ['office', 'name']
You can try following inside your model class
class Meta:
unique_together = [('office', 'name')]
I'm using Django built in UserCreationForm. I want to show message under the field when that field is empty and user tring to submit form. Unfortunatly I see only built-in behavior of browsers like "fill out this field", by the way in different browsers that behavior is different. Some browsers just encircle the field box with a red line. How to turn off this behavior and show message under the field. Why .error_messages didnt work?! Also I use {{ form.field_name.errors }} in my template.
forms.py
class RegistrationForm(UserCreationForm):
required_css_class = 'required'
email = forms.EmailField()
first_name = forms.CharField()
last_name = forms.CharField()
class Meta:
model = User
fields = ('username', 'email', 'first_name', 'last_name')
def __init__(self, *args, **kwargs):
super(RegistrationForm, self).__init__(*args, **kwargs)
self.fields['username'].widget = TextInput(attrs={'placeholder': _('Username')})
self.fields['username'].required = True
self.fields['username'].error_messages = {'required': 'Please enter your username'}
self.fields['email'].widget = EmailInput(attrs={'placeholder': _('Email address')})
self.fields['email'].required = True
self.fields['email'].error_messages = {'required': 'Please enter your email'}
self.fields['first_name'].widget = TextInput(attrs={'placeholder': _('First name')})
self.fields['first_name'].required = True
self.fields['first_name'].error_messages = {'required': 'Please enter your first_name'}
self.fields['last_name'].widget = TextInput(attrs={'placeholder': _('Last name')})
self.fields['last_name'].required = True
self.fields['last_name'].error_messages = {'required': 'Please enter your last_name'}
self.fields['password1'].widget = PasswordInput(attrs={'placeholder': _('Password')})
self.fields['password1'].required = True
self.fields['password1'].error_messages = {'required': 'Please enter your Password'}
self.fields['password2'].widget = PasswordInput(attrs={'placeholder': _('Confirm password')})
self.fields['password2'].required = True
self.fields['password2'].error_messages = {'required': 'Please enter your Confirm Password'}
view.py
class RegistrationView(FormView):
disallowed_url = 'registration_closed'
form_class = RegistrationForm
http_method_names = ['get', 'post', 'head', 'options', 'trace']
success_url = 'registration_complete'
template_name = 'account/registration_form.html'
SEND_ACTIVATION_EMAIL = getattr(settings, 'SEND_ACTIVATION_EMAIL', True)
registration_profile = RegistrationProfile
#method_decorator(sensitive_post_parameters('password1', 'password2'))
def dispatch(self, request, *args, **kwargs):
if not self.registration_allowed():
return redirect(self.disallowed_url)
return super(RegistrationView, self).dispatch(request, *args, **kwargs)
def form_valid(self, form):
new_user = self.register(form)
success_url = self.get_success_url(new_user)
try:
to, args, kwargs = success_url
except ValueError:
return redirect(success_url)
else:
return redirect(to, *args, **kwargs)
def registration_allowed(self):
return getattr(settings, 'REGISTRATION_OPEN', True)
def register(self, form):
site = get_current_site(self.request)
if hasattr(form, 'save'):
new_user_instance = form.save()
else:
new_user_instance = (UserModel().objects.create_user(**form.cleaned_data))
new_user = self.registration_profile.objects.create_inactive_user(
new_user=new_user_instance,
site=site,
send_email=self.SEND_ACTIVATION_EMAIL,
request=self.request,
)
signals.user_registered.send(sender=self.__class__, user=new_user, request=self.request)
return new_user
def get_success_url(self, user=None):
return super(RegistrationView, self).get_success_url()
You need to turn off the behaviour in the HTML itself, by using novalidate in the form element.
<form action="whatever" method="POST" novalidate>
...
</form>