I would like to use a custom form field with an UpdateView. My code so far is:
models.py
class CustomUser(AbstractUser):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
paper = models.BooleanField(default=False)
pronouns = models.CharField(max_length=50, blank=True)
def __str__(self):
return self.username
forms.py
class CustomUserProfileForm(forms.ModelForm):
pronouns = forms.CharField(label='Pronouns', required=True)
class Meta:
model = get_user_model()
fields = ('first_name', 'last_name')
def __init__(self, *args, **kwargs):
_pronoun_list = ('he/him/his', 'she/her/hers', 'they/them/theirs')
super(CustomUserProfileForm, self).__init__(*args, **kwargs)
self.fields['pronouns'].widget = ListTextWidget(
data_list=_pronoun_list, name='pronoun-list')
This form references ListTextWidget although I don't think it directly relates to my issue:
class ListTextWidget(forms.TextInput):
def __init__(self, data_list, name, *args, **kwargs):
super(ListTextWidget, self).__init__(*args, **kwargs)
self._name = name
self._list = data_list
self.attrs.update({'list': 'list__%s' % self._name})
def render(self, name, value, attrs=None, renderer=None):
text_html = super(ListTextWidget, self).render(
name, value, attrs=attrs)
data_list = '<datalist id="list__%s">' % self._name
for item in self._list:
data_list += '<option value="%s">' % item
data_list += '</datalist>'
return (text_html + data_list)
Views.py
class CustomUserProfileview(UpdateView):
model = CustomUser
form_class = CustomUserProfileForm
template_name = 'account/customuser_change_form.html'
success_url = reverse_lazy('home')
def form_valid(self, form):
user = self.request.user
pronouns = form.cleaned_data['pronouns']
user.pronouns = pronouns
user.save()
return super().form_valid(form)
The form is displayed as I would expect, where I can change the first name, last name and choose pronouns from a list. If I print(user) and print(pronouns) I get my expected results, but the pronouns field is not being saved to the user. Even if it did save, I get the feeling that I'm not leveraging the UpdateView correctly.
Related
class Post(models.Model):
cat_post = models.ForeignKey(Category, on_delete=models.CASCADE, blank=True,null=True)
top_post = models.ForeignKey(TopicsCategory, on_delete=models.CASCADE, blank=True,null=True)
sub_post = models.ForeignKey(SubTopicsCategory, on_delete=models.CASCADE, blank=True,null=True)
class CreatePostView(CreateView):
model = Post
template_name = 'blog/create.html'
form_class = CreatePostForm
def get_context_data(self, *args, **kwards):
print(self.kwargs)
context = super(CreatePostView, self).get_context_data(**kwards)
context['btn'] = 'Add'
return context
def form_valid(self, form, *args, **kwargs):
if self.kwargs.get('category_slug') and len(self.kwargs) == 1:
category = Category.objects.get(slug=self.kwargs['category_slug'])
form.instance.cat_post = category
return super(CreatePostView, self).form_valid(form)
# передача в форму kwargs view
def get_form_kwargs(self):
kwargs = super(CreatePostView, self).get_form_kwargs()
kwargs.update({'view_kwargs': self.kwargs})
return kwargs
def get_success_url(self):
return reverse('topics_category_list', kwargs={'category_slug': self.kwargs['category_slug'], })
class CreatePostForm(forms.ModelForm):
class Meta:
model = Post
fields = ['name', 'text', 'discussion']
# widgets = {
# 'cat_post': forms.HiddenInput(),
# }
def __init__(self, *args, **kwargs):
self.request = kwargs.pop("view_kwargs")
super(CreatePostForm, self).__init__(*args, **kwargs)
def clean_name(self):
name = self.cleaned_data['name']
if self.request.get('category_slug') and len(self.request) == 1:
category = Category.objects.get(slug=self.request['category_slug'])
unique = Post.objects.filter(slug=slugify(self.cleaned_data['name']), cat_post=category.pk,discussion=False).exists()
if unique:
raise ValidationError(f'Post is not unique')
return name
**
I have duplicate sources in db here form_valid and clean_name.
How do I pass the form in the view form form_valid class instance that I got from the database to clean_name.
There will be posts for 2 models and requests will increase
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 created an update screen for django. However, although it redirects to successURL, the data has not been updated. I don't know why.
I need your help.
I will post it if necessary.
#view
class RecordDetailEdit(UpdateView,LoginRequiredMixin):
template_name = 'records/detail_edit.html'
model = URC
form_class = RecordDetailEditForm
pk_url_kwarg = 'id'
success_url = reverse_lazy('person:home')
def get_object(self):
return get_object_or_404(User, pk=self.request.user.user_id)
def get_form_kwargs(self):
kwargs = super(RecordDetailEdit, self).get_form_kwargs()
# get users, note: you can access request using: self.request
kwargs['user'] = self.request.user
return kwargs
#form
class RecordDetailEditForm(forms.ModelForm):
class Meta:
model = URC
fields = ('UPRC','URN','UET','URT')
def __init__(self, *args, **kwargs):
user = kwargs.pop('user')
super(RecordDetailEditForm, self).__init__(*args, **kwargs)
self.fields['URN'].queryset = UPRM.objects.filter(user=user)
#model
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)
UET = models.DurationField(editable=True)
URT = models.DateTimeField(default=timezone.now,editable=True)
group = models.ForeignKey(group, on_delete=models.CASCADE, null=True)
def __str__(self):
return self.UPRC
#url
path('<id>/edit/', views.RecordDetailEdit.as_view(), name='record_detail_edit'),
I changed it to the following.
def get_object(self, queryset=None):
obj = URC.objects.get(id=self.kwargs['id'])
return obj
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')
The problem is that I have a model secciones that is associated with a user and a productos model that is also associated with a user and model secciones,
class secciones(models.Model):
name = models.CharField(max_length=50)
user = models.ForeignKey(User)
def save(self, *args, **kwargs):
if not self.id:
self.slug = slugify(self.name)
super(secciones, self).save(*args, **kwargs)
def __unicode__(self):
return self.name
class productos(models.Model):
user = models.ForeignKey(User)
secciones = models.ForeignKey(secciones)
name = models.CharField(max_length=50)
image = models.ImageField(upload_to = 'productos')
precio = models.DecimalField(max_digits=10, decimal_places=2, default=0.00)
descripcion = models.TextField(max_length=300, null=True,blank=True)
def save(self, *args, **kwargs):
if not self.id:
self.slug = slugify(self.name)
super(productos, self).save(*args, **kwargs)
def __unicode__(self):
return self.name
I am creating a form so that the user can enter a new product in the productos model, but I just want to leave the sections of the model secciones of the user that I log in
This is the view
def agregar_producto(request):
if request.method == "POST":
modelform = AgregarProducto(request.POST,request.FILES)
print modelform
if modelform.is_valid():
modelform.save()
return redirect("/editar-perfil/")
else:
modelform = AgregarProducto()
return render(request, "home/AgregarProducto.html", {"form":modelform})
This is the form
class AgregarProducto(forms.ModelForm):
class Meta:
model = productos
How can I get the form to display only the sections of the model secciones of the user that logged in
Override the __init__ method of your model, and set the queryset for your secciones field.
class AgregarProducto(forms.ModelForm):
class Meta:
model = productos
def __init__(self, *args, **kwargs):
user = kwargs.pop('user')
super(AgregarProducto, self).__init__(*args, **kwargs)
self.fields['secciones'].queryset = secciones.objects.filter(user=user)
Now update your view so that you pass the user when instantiating the form. You need to do this for GET and POST requests.
if request.method == "POST":
modelform = AgregarProducto(request.POST, request.FILES, user=request.user)
...
else:
modelform = AgregarProducto(user=request.user)
You can use the login_required decorator, so that only logged in users can access the view.
from django.contrib.auth.decorators import login_required
#login_required
def agregar_producto(request):
Finally, please rename your models to Secciones and Productos. The lowercase secciones and productos look very unusual to most Django users, and can be confused with model instances.