I have created this Django model:
class Teacher(models.Model):
user = models.OneToOneField('auth.User', on_delete=models.CASCADE, default=1)
surname = models.CharField(max_length=15, null=True)
creation_date = models.DateTimeField('creation date', auto_now_add=True)
deletion_date = models.DateTimeField('deletion date', null=True, blank=True)
and a form to represent it in read-only mode:
class TeacherViewForm(ModelForm):
def __init__(self, *args, **kwargs):
super(TeacherViewForm, self).__init__(*args, **kwargs)
instance = getattr(self, 'instance', None)
if instance and instance.pk:
for field in self.fields:
self.fields[field].widget.attrs['disabled'] = True
class Meta:
model = Teacher
exclude = ['deletion_date']
The view looks like this:
class TeacherViewDetail(generic.DetailView):
model = Teacher
template_name = 'control/teacher_detail.html'
form_class = TeacherViewForm
def get_form(self):
# Instantiate the form
form = self.form_class(instance=self.object)
return form
def get_context_data(self, **kwargs):
context = super(TeacherViewDetail, self).get_context_data(**kwargs)
context.update({
'form': self.get_form(),
})
return context
As you can see, there is a OneToOne relation between the Teacher and the auth.User models. I need to displayfirst_name and last_name from auth.User model, but only the username is shown.
How can I display these fields the same way field Teacher.surname is being displayed?
Do I have to include them in the model, the form.fields or is there a property I have to modify in order to achieve it?
Thanks
You would have to remove the user field and add the appropriate fields instead. Then in your form's __init__, fill the initial values:
# forms.py
class TeacherViewForm(ModelForm):
first_name = forms.CharField()
last_name = forms.CharField()
def __init__(self, *args, **kwargs):
super(TeacherViewForm, self).__init__(*args, **kwargs)
instance = getattr(self, 'instance', None)
if instance and instance.pk:
for field in self.fields:
self.fields[field].widget.attrs['disabled'] = True
# set initial values
self.fields['first_name'].initial = instance.user.first_name
self.fields['last_name'].initial = instance.user.last_name
class Meta:
model = Teacher
exclude = ['user', 'deletion_date'] # exclude user
But why do you use a form at all if you disable all fields? You can just render all this information much more freely in the template.
Related
Firstly, I extend AbstractUser like this:
class MyProfile(AbstractUser):
first_name = models.CharField(max_length=100)
last_name = models.CharField(max_length=100)
full_name = models.CharField(max_length=255)
id_number = models.CharField(max_length=14)
def __str__(self):
return self.full_name
def save(self, *args, **kwargs):
self.full_name = '{0} {1}'.format(self.first_name, self.last_name)
super().save(*args, **kwargs)
And then reference to other model like this:
class MyModel(models.Model):
member = models.OneToOneField(MyProfile, on_delete=models.CASCADE)
description = models.CharField(max_length=255)
def __str__(self):
return self.member
And this the form:
class MyModelForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
class Meta:
model = MyModel
fields = ('description')
full_name = forms.CharField(required=False, label="", widget=forms.TextInput(attrs={'class': "form-control"}))
id_number = forms.CharField(required=False, label="", widget=forms.TextInput(attrs={'class': "form-control"}))
description = forms.CharField(required=False, label="", widget=forms.TextInput(attrs={'class': "form-control"}))
And this is the view:
def applicationdata(request):
if request.method == 'POST':
form = MyModelForm(request.POST or None)
if form.is_valid():
form.save()
return HttpResponseRedirect('/index/')
else:
print(form.errors)
else:
form = MyModelForm()
context = {
'form': form,
}
return render(request, 'index.html', context)
My question is:
How to show full_name's field & id_number's field instead of member's field on MyModelForm?
And how to save that fields via applicationdata's views into member's field in MyModel's model?
UPDATE
I updated the AbstractUser model because I forget to add first_name & last_name and I forget to mention if I use allauth
I updated the form with init
Instead of using ModelForm it's better to use Form here because certain we don't particularly use the field of models.
Your forms.py should like this
class MyModelForm(forms.Form):
full_name = forms.CharField(required=False, label="", widget=forms.TextInput(attrs={'class': "form-control"}))
id_number = forms.CharField(required=False, label="", widget=forms.TextInput(attrs={'class': "form-control"}))
description = forms.CharField(required=False, label="", widget=forms.TextInput(attrs={'class': "form-control"}))
while our views method should need to be totally tailored according to fields and data need to be stored.
Change your views.py method to this
def applicationdata(request):
if request.method == 'POST':
form = MyModelForm(request.POST or None)
if form.is_valid():
profile = MyProfile.object.create(full_name=request.POST['full_name'], id_number=request.POST['id_number'])
if profile is not None:
MyModel.object.create(description=request.POST['description'], member=profile.pk)
return HttpResponseRedirect('/index/')
else:
form.add_error('profile_out_of_bound', "please try to fill correct details!")
print(form.errors)
else:
form = MyModelForm()
context = {
'form': form,
}
return render(request, 'index.html', context)
Correct me if this doesn't work.
first you can remove first_name and last_name field because you can get it from full_name
class MyProfile(AbstractUser):
full_name = models.CharField(max_length=255)
id_number = models.CharField(max_length=14)
def __str__(self) -> str:
return self.full_name
#property
def first_name(self) -> str:
return self.full_name.split(' ')[0]
#property
def last_name(self) -> str:
if self.full_name.split(' ') > 1:
return self.full_name.split(' ')[1]
return ''
class MyModelForm(forms.ModelForm):
full_name = forms.CharField(required=False, label="", widget=forms.TextInput(attrs={'class': "form-control"}))
id_number = forms.CharField(required=False, label="", widget=forms.TextInput(attrs={'class': "form-control"}))
class Meta:
model = MyModel
fields = ('description')
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['description'].widget = forms.TextInput(attrs={'class': "form-control"})
def save(self, *args, **kwargs):
kwargs['commit'] = False
my_model = super().save(*args, **kwargs)
MyProfile.objects.update_or_create(
full_name=self.cleaned_data['full_name'],
id_number=self.cleaned_data['id_number'],
mymodel=my_model
)
if the MyProfile object already exists, you can use it to fill in the form fields automatically with __init__
def __init__(self, my_profile: MyProfile, *args, **kwargs):
kwargs.update(initial={
# 'field': 'value'
'full_name': my_profile.full_name,
'id_number': my_profile.id_number
})
super().__init__(*args, **kwargs)
self.fields['description'].widget = forms.TextInput(attrs={'class': "form-control"})
How to show full_name's field & id_number's field instead of member's field on MyModelForm?
if you mean to show it to change it afterwards you have to override the __init__ method to set the initial data on the two fields: maybe this answer can help
And how to save that fields via applicationdata's views into member's field in MyModel's model?
entity = form.save() # this gives you the updated MyModel instance
then save the data from the form to the related model, something like:
entity.member.full_name = form.cleaned_data['full_name']
should work according to: this answer
I want to use uniqueness validation in class-based views.
Here I have createView where I want to add part_no uniqueness validation at the time of form post.
How can we achieve this?
Any solutions.
Views.py
class SparePartsCreate(CreateView):
template = 'maint/spareparts_form.html'
model = SpareParts
fields = ['name', 'description', 'part_no']
success_url = reverse_lazy('spare_parts')
form.py
class SparePartForm(forms.ModelForm):
name = forms.CharField(required=True, label='Spare Part Name')
description = forms.CharField(required=True, label='Spare Part Description')
part_no = forms.CharField(required=True, label='Spare Part Number', max_length=6)
class Meta:
model = SpareParts
fields = ['name','description','part_no']
def __init__(self, *args, **kwargs):
self.request = kwargs.pop('request', None)
super(SparePartForm, self).__init__(*args, **kwargs)
As we implement validation in function-based views in form.py under clean method.
For class-based views anything is there?
def clean_part_no(self):
part_no = self.cleaned_data.get('part_no')
qs = SpareParts.objects.filter(part_no=part_no)
if qs.exists():
raise forms.ValidationError('This part_no already taken, please use a different one.')
return part_no
either use this way in your forms.py or make it as unique=True in models.py file
class SparePartForm(forms.ModelForm):
name = forms.CharField(required=True, label='Spare Part Name')
description = forms.CharField(required=True, label='Spare Part Description')
part_no = forms.CharField(required=True, label='Spare Part Number', max_length=6)
class Meta:
model = SpareParts
fields = ['name','description','part_no']
def __init__(self, *args, **kwargs):
self.request = kwargs.pop('request', None)
super(SparePartForm, self).__init__(*args, **kwargs)
def clean_part_no(self, value):
""" here you can validate the data """
if True:
return value
else:
raise forms.validationError("error message ")
Although it is in the title, I want to change the form dynamically with django.
But now I get an error.
I can't deal with it.
I was able to get user information, but if I filter it, it will be “cannot unpack non-iterable UPRM object”.
#forms.py
class RecordCreateForm(BaseModelForm):
class Meta:
model = URC
fields = ('UPRC','URN','UET','URT',)
def __init__(self, *args, **kwargs):
user = kwargs.pop('user')
super(RecordCreateForm,self).__init__(*args, **kwargs)
for field in self.fields.values():
field.widget.attrs['class'] = 'form-control'
self.fields['URN'].choices = UPRM.objects.filter(user=user)
#views.py
class RecordCreate(CreateView):
model = URC
form_class = RecordCreateForm
template_name = 'records/urcform.html'
success_url = reverse_lazy('person:home')
def get_form_kwargs(self):
kwargs = super(RecordCreate, self).get_form_kwargs()
# get users, note: you can access request using: self.request
kwargs['user'] = self.request.user
return kwargs
#models
class UPRM(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
URN = models.CharField( max_length=30,editable=True)
def __str__(self):
return self.URN
class URC(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
UPRC = models.CharField(max_length=300)
URN = models.ForeignKey(UPRM, on_delete=models.CASCADE)
def __str__(self):
return self.UPRC
cannot unpack non-iterable UPRM object
You should use queryset instead of choices here:
class RecordCreateForm(BaseModelForm):
class Meta:
model = URC
fields = ('UPRC','URN','UET','URT',)
def __init__(self, *args, **kwargs):
user = kwargs.pop('user')
super(RecordCreateForm,self).__init__(*args, **kwargs)
for field in self.fields.values():
field.widget.attrs['class'] = 'form-control'
self.fields['URN'].queryset = UPRM.objects.filter(user=user)
I would like to know how I can do that : save an object in my database through Django model with custom field.
This is my modelclass :
class Document(EdqmTable):
language = models.CharField(max_length=2, verbose_name=_('language'), choices=LANGUAGE_CHOICES, null=False)
format = models.CharField(max_length=10, verbose_name=_('format'), choices=FORMAT_CHOICES, null=False)
title = models.CharField(max_length=512, verbose_name=_('document title'), null=False)
publication = models.ForeignKey(Publication, verbose_name=_('publication title'), null=False,
related_name='documents')
class Meta:
verbose_name = _('document')
verbose_name_plural = _('documents')
def save(self, *args, **kwargs):
self.title = f"{self.publication.pub_id}-{self.format.upper()}"
super(Document, self).save(*args, **kwargs)
I have a save method, which let to define my field title with combination between two fields.
title field is hidden in my django form and is set automatically by Django.
But, up to now, it doesn't work because my object is not saved into my database. Something is wrong in my save function ?
EDIT :
I edited my post with forms.py file and my view :
The field document.title is used in my django forms with formset like this :
class PublicationForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['category'].empty_label = _('Select a category') # Modify initial empty_label
class Meta:
model = Publication
fields = ['title', 'pub_id', 'category']
class DocumentForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(DocumentForm, self).__init__(*args, **kwargs)
self.fields['title'].widget = forms.HiddenInput()
for key in self.fields:
self.fields[key].required = True
class Meta:
model = Document
fields = ['publication', 'language', 'format', 'title', 'upload']
DocumentFormSet = inlineformset_factory(Publication, Document, form=DocumentForm, extra=1)
And my view according to this part :
class PublicationCreateView(EdqmCreateView):
""" Create publication with document form through formset """
model = Publication
template_name = 'freepub/publication_form.html'
def get_context_data(self, **kwargs):
context = super(PublicationCreateView, self).get_context_data(**kwargs)
context['document_form'] = DocumentFormSet(self.request.POST or None, self.request.FILES or None)
return context
def form_valid(self, form):
context = self.get_context_data()
document = context['document_form']
if document.is_valid():
self.object = form.save()
document.instance = self.object
document.save()
return super(PublicationCreateView, self).form_valid(form)
def get_success_url(self):
return reverse('publication-list-crud')
I extended my django user and need to create a registration form now.
I got most of it figured out but I don't know how to exclude fields I don't need during registration. Right know I see all fields in the registration form.
Here is the code:
models.py
class Artist(Model):
user = OneToOneField(User, unique=True)
address = CharField(max_length=50)
city = CharField(max_length=30)
ustid = CharField(max_length=14)
date_of_birth = DateField()
bio = CharField(max_length=500)
def __unicode__(self):
return self.user.get_full_name()
User.profile = property(lambda u: Artist.objects.get_or_create(user=u)[0])
forms.py
class RegistrationForm(UserCreationForm):
class Meta:
model = User
def __init__(self, *args, **kwargs):
super(RegistrationForm, self).__init__(*args, **kwargs)
artist_kwargs = kwargs.copy()
if kwargs.has_key('instance'):
self.artist = kwargs['instance'].artist
artist_kwargs['instance'] = self.artist
self.artist_form = ArtistForm(*args, **artist_kwargs)
self.fields.update(self.artist_form.fields)
self.initial.update(self.artist_form.initial)
def clean(self):
cleaned_data = super(RegistrationForm, self).clean()
self.errors.update(self.artist_form.errors)
return cleaned_data
def save(self, commit=True):
self.artist_form.save(commit)
return super(RegistrationForm, self).save(commit)
How do I exclude fields?
class Meta:
model = User
exclude = ('bio',)
You can't include or exclude fields that are not a member of the meta model.
What you can do is doing that in each form. In this case the UserCreationForm is extended by the ArtistForm. Just restrict the fields in the form that belong to the right meta model.