Django: Add extra attributes to form fields generated by UpdateView - django

Am using a custom user that is a subclass of Django AbstractUser, what am trying to archive is to allow user update their data everything works but the form look ugly. Below is my code the class attribute is not added to the form.
forms.py(simplified)
class AccountEditForm(forms.ModelForm):
class Meta:
model = CustomUser
fields = ('first_name', 'last_name', 'phone_number', 'date_of_birth', 'country')
widget = {
'first_name':forms.TextInput(
attrs={
'class': 'input-bordered',
}
)
}
views.py
class UserAccountDetails(LoginRequiredMixin, UpdateView):
template_name = 'dashboard/account_edit.html'
context_object_name = 'form'
form_class = AccountEditForm
model = CustomUser
def get_object(self, queryset=None):
"""
Return the object the view is displaying.
"""
if queryset is None:
queryset = self.get_queryset()
#Get logged in user from request data
queryset = queryset.filter(pk=self.request.user.id)
try:
# Get the single item from the filtered queryset
obj = queryset.get()
except queryset.model.DoesNotExist:
raise Http404(_("No %(verbose_name)s found matching the query") %
{'verbose_name': queryset.model._meta.verbose_name})
return obj

The widgets option is for overriding the defaults on explicitly declared fields. To add class to the field you have many options.
Option #1: Explicitly declare form field and add class through widgets in Meta.
class AccountEditForm(forms.ModelForm):
first_name = forms.TextField(widget=forms.TextInput())
class Meta:
model = CustomUser
fields = ('first_name', 'last_name', 'phone_number', 'date_of_birth', 'country')
widgets = {
'first_name': forms.TextInput(
attrs={
'class': 'input-bordered',
}
)
}
Option #2: Shorter version of option #1.
class AccountEditForm(forms.ModelForm):
first_name = forms.TextField(widget=forms.TextInput(attrs={'class': 'input-bordered'}))
class Meta:
model = CustomUser
...
Option #3: Add class in form's __init__ method.
class AccountEditForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(AccountEditForm, self).__init__(*args, **kwargs)
self.fields['first_name'].widget.attrs['class'] = 'input-bordered'
Option #4: Use django-widget-tweaks plugin.

Related

Setting some fields to be automatically filled in using CreateVeiw in Django

I'm using Django's CreateView in order to fill a form and I want some of the fields to be automatically filled in, looking for ides how I could that. the fields that I want to be filled in automatically are company, recruiter and date
this is what the views file looks like:
class CreateNewJobForm(CreateView):
model = Job
fields = (
'title', 'company', 'recruiter', 'job_type', 'work_from', 'description', 'city', 'address', 'title_keywords',
'date_created')
template_name = 'create_new_job_form.html'
success_url = '/job_created_successfully'
def form_valid(self, form):
form.instance.recruiter = self.get_name()
return super(CreateNewJobForm, self).form_valid(form)
and this is what the models file looks like:
class Recruiter(models.Model):
user = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, primary_key=True)
name = models.CharField(max_length=255)
company = models.ForeignKey(Company, on_delete=models.RESTRICT, related_name='recruiters')
email = models.EmailField(max_length=255)
phone_number = models.CharField(max_length=15, blank=True)
Something like that should work just fine :
def form_valid(self, form):
form.instance.user = self.request.user # assuming you want the current login user to be set to the user
return super(CreateNewJobForm, self).form_valid(form)
It is just an example but in short you can access attributes of your model by accessing the instance of your form like that form.instance.yourfield
Automatically assign the value
We can assign a value without showing this in the form. In that case, you remove the company, recruiter, and date_created fields from the fields, and fill these in in the form_valid method:
from django.contrib.auth.mixins import LoginRequiredMixin
class CreateNewJobForm(LoginRequiredMixin, CreateView):
model = Job
fields = ('title', 'job_type', 'work_from', 'description', 'city', 'address', 'title_keywords')
template_name = 'create_new_job_form.html'
success_url = '/job_created_successfully'
def form_valid(self, form):
recruiter = form.instance.recruiter = self.request.user.recruiter
form.instance.company_id = recruiter.company_id
return super().form_valid(form)
for the date_created, you can work with the auto_now_add=True parameter [Django-doc] of the DateTimeField:
class Job(models.Model):
# …
date_created = models.DateTimeField(auto_now_add=True)
Provide an initial value
We can also provide an initial value for the form by overriding the .get_initial() method [Django-doc]:
from django.contrib.auth.mixins import LoginRequiredMixin
from django.utils.timezone import now
class CreateNewJobForm(LoginRequiredMixin, CreateView):
model = Job
fields = ('title', 'job_type', 'work_from', 'description', 'city', 'address', 'title_keywords', 'company', 'recruiter', 'date_created')
template_name = 'create_new_job_form.html'
success_url = '/job_created_successfully'
def get_initial(self):
recruiter = self.request.user.recruiter
return {
'recruiter': recruiter,
'company': recruiter.company,
'date_created': now()
}

How to use a form with autocomplete fields in django admin update action

I am using a custom form in admin panel with two autocomplete fields among the others.
My problem is that I don't know how to use the form in update action in order the stored data to appear with the autocomplete functionality.
In my implementation in update action the values appearing without autocomplete functionality.
How can I fix that?
my form
class ModelSeoMetadatumForm(forms.ModelForm):
name = ModelChoiceField(
required=False,
queryset=MetaTag.objects.exclude(name__isnull=True).values_list('name', flat=True).distinct(),
widget=autocomplete.ModelSelect2(url='seo:name-autocomplete')
)
property = ModelChoiceField(
required=False,
queryset=MetaTag.objects.exclude(property__isnull=True).values_list('property', flat=True).distinct(),
widget=autocomplete.ModelSelect2(url='seo:property-autocomplete')
)
class Meta:
model = ModelSeoMetadatum
fields = ('name', 'content', 'property', 'content_type', 'object_id')
my admin
#admin.register(ModelSeoMetadatum)
class ModelSeoMetadatumAdmin(admin.ModelAdmin):
add_form = ModelSeoMetadatumForm
list_display = ('id', 'name', 'content', 'property', 'content_object')
fields = ('name', 'content', 'property', 'content_type', 'object_id')
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 should overwrite the widget and give it the admin site as parameter.
admin class:
class MyAdmin(admin.ModelAdmin):
form = MyForm
form definition:
class MyForm(forms.ModelForm):
class Meta:
widgets = {
'some_lookup_field': AutocompleteSelect(
MyModel._meta.get_field('some_lookup_field').remote_field,
admin.site,
attrs={'style': 'width: 20em'},
),
}
Note, you need to have at lease one search_filter in the admin definition of your lookup field.
Have a look here for an improved version that expands if needed link

how to add search_fields in django forms

I was making a django forms and there is a field owner which is related with ForeignKey by User model , Sometimes name of user is same so I want to search it by their email address , How can I add searching of email field in forms like this search_fields = ['email'].
class GroupForm(forms.ModelForm):
class Meta:
model = Group
fields = ('name', 'owner', 'club', 'moderator', 'group_type', 'country')
def __init__ (self, *args, **kwargs):
# brand = kwargs.pop("brand")
super(GroupForm, self).__init__(*args, **kwargs)
language_results = User.objects.all()
# self.fields["owner"].widget = forms.widgets.CheckboxSelectMultiple()
# self.fields["owner"].widget = autocomplete.ModelSelect2()
self.fields["owner"] = forms.ModelMultipleChoiceField(
queryset=User.objects.all(),
required=True,
widget = forms.SelectMultiple(attrs={
'placeholder': "Choose the users(s)",
'class': 'chzn-select',
'multiple tabindex': '6',
}))
you just use a CharField and add that form in your template;and after submiting that search you get the value entred by the user and use the objects filter to return the corresponding result.

How to save a modelSerializer that has relations? - django

I want to save a sent json data to db by django-rest-framework.
the problem is, not saving the relation and returns error.
The bellow snippet is my models:
class Profile(models.Model):
user = models.OneToOneField(settings.AUTH_USER_MODEL, related_name='profile', on_delete=models.CASCADE)
name = models.CharField(max_length=30)
family = models.CharField(max_length=50)
class Klass(models.Model):
title = models.CharField(max_length=50)
description = models.CharField(max_length=500)
teacher = models.ForeignKey(Profile, related_name='teacher', on_delete=models.CASCADE)
I use below serializer for serializing/deserializing the Klass model.
class ProfileSerializer(serializers.ModelSerializer):
class Meta:
model = Profile
fields = ('pk', 'name', 'family')
class KlassSerializer(serializers.ModelSerializer):
teacher = ProfileSerializer()
class Meta:
model = Klass
fields = ('id', 'title', 'description', 'teacher')
now when I prepare a JSON object and send it to the view, it returns error. the below is the view class:
class KlassView(APIView):
"""for SELECT, INSERT Queries"""
def get(self, request, pk):
# somthing
#csrf_exempt
def post(self,request, pk=None):
"""For Creating A Class"""
serializer = KlassSerializer(data=request.data)
if serializer.is_valid():
teacher = ProfileSerializer(request.data['teacher']['pk'])
serializer.teacher = teacher.data
serializer.save()
return Response({'data': serializer.data})
else:
return Response({'data': serializer.errors})
and the error is:
The .create() method does not support writable nested fields by default.
Write an explicit .create() method for serializer mainp.serializers.KlassSerializer, or set read_only=True on nested serializer fields.
How can I save relation in KlassSerializer in order to save to db?
At first change your serializer like below:
class KlassSerializer(serializers.ModelSerializer):
# teacher = ProfileSerializer() # No need to this!
class Meta:
model = Klass
# fields = ('id', 'title', 'description', 'teacher')
fields = ('id', 'title', 'description') # Omit teacher
Then get profile from requested user and pass it to your serializer:
def post(self,request, pk=None):
"""For Creating A Class"""
serializer = KlassSerializer(data=request.data)
if serializer.is_valid():
teacher = ProfileSerializer(request.data['teacher']['pk'])
serializer.teacher = teacher.data
serializer.save(teacher=request.user.profile) # Retrieve teacher and stroe
return Response({'data': serializer.data})
else:
return Response({'data': serializer.errors})
Just override the create method of ModelSerializer in KlassSerializer.
class KlassSerializer(serializers.ModelSerializer):
teacher = ProfileSerializer()
class Meta:
model = Klass
fields = ('id', 'title', 'description', 'teacher')
def create(self, validated_data):
profile = Profile.objects.filter(pk=validated_data['teacher']['pk'])
if profile:
k = Klass()
k.teacher = profile
...

conditionally change widget type in django form

I have the following simple form:
class ContactEmailForm(forms.ModelForm):
subject = forms.ChoiceField(choices=SUBJECT_TYPES)
class Meta:
model = ContactEmail
fields = ('name', 'email', 'subject', 'message',)
I want to conditionally change the subject field between a choice field and text input field.
How can I do this?
This could be accomplished by overriding the __init__ function within your ContactEmailForm class.
class ContactEmailForm(forms.ModelForm):
subject = forms.ChoiceField(choices=SUBJECT_TYPES)
def __init__(self, *args, **kwargs):
super(ContactEmailForm, self).__init__(*args, **kwargs)
if YOURCONDITION:
self.fields['subject'] = forms.CharField()
class Meta:
model = ContactEmail
fields = ('name', 'email', 'subject', 'message',)