I have an UpdateUserForm:
class UserUpdateForm(UserChangeForm):
email = forms.EmailField()
first_name = forms.CharField(max_length=100)
last_name = forms.CharField(max_length=100)
username = forms.CharField(max_length=100, widget=forms.TextInput())
last_login = forms.CharField(max_length=100, widget=forms.TextInput(attrs={'type': 'hidden'}))
is_superuser = forms.CharField(max_length=100, widget=forms.CheckboxInput(attrs={'type': 'hidden'}))
is_staff = forms.CharField(max_length=100, widget=forms.CheckboxInput(attrs={'type': 'hidden'}))
is_active = forms.CharField(max_length=100, widget=forms.CheckboxInput(attrs={'type': 'hidden'}))
date_joined = forms.CharField(max_length=100, widget=forms.TextInput(attrs={'type': 'hidden'}))
class Meta:
model = User
fields = ('username', 'first_name', 'last_name', 'email', 'last_login', 'date_joined')
def update_user_notification(self):
email = self.cleaned_data['email']
username = self.cleaned_data['username']
if self.is_valid():
update_user_notification_task.delay(email, username)
and a UserUpdate view:
class UserUpdate(generic.UpdateView):
model = User
form_class = UserUpdateForm
template_name = 'accounts/update_user.html'
def get_object(self):
return self.request.user
def form_valid(self, form):
instance = form.save()
form.update_user_notification()
return HttpResponseRedirect(reverse('user_detail', args=[str(instance.pk)]))
I originally wrote this form in the template as {{form.as_p}} and it worked, but there were some things I wanted to fix:
<h1>Update Information...</h1>
<form method="POST">
{% csrf_token %}
{{form.as_p}}
<button>UPDATE</button>
</form>
<br></br>
Back
Home
{% endblock %}
Rendered as this:
I wanted to get rid of those password related things at the bottom so I changed my form to:
<form method="POST">
{% csrf_token %}
<p><label>{{form.email.label}}: </label>{{form.email}}</p>
<p><label>{{form.username.label}}: </label>{{form.username}}</p>
<p><label>{{form.first_name.label}}: </label>{{form.first_name}}</p>
<p><label>{{form.last_name.label}}: </label>{{form.last_name}}</p>
<button>UPDATE</button>
</form>
This worked on the frontend, but now my form isn't submitting. When I click update, the page looks as if it's loading, but my database doesn't updating and my celery task isn't run. What's the issue?
Do I need to have all the fields in order to submit the form? Is there a way to just exclude the password related fields? And is that a better option?
The answer was actually really simple. I added {{form.errors}} to see if any errors were occurring in the actual form, since there were no errors in my network tab.
Turns out that last_login and date_joined are both required in order to POST the form. Once I added them in:
<form method="POST">
{% csrf_token %}
<p><label>{{form.email.label}}: </label>{{form.email}}</p>
<p><label>{{form.username.label}}: </label>{{form.username}}</p>
<p><label>{{form.first_name.label}}: </label>{{form.first_name}}</p>
<p><label>{{form.last_name.label}}: </label>{{form.last_name}}</p>
<p>{{form.last_login}}</p>
<p>{{form.date_joined}}</p>
<button>UPDATE</button>
</form>
The form was able to submit and update the database. I left out the label because those forms for me are attrs={'type': 'hidden'} anyway. Although this brings up another question as to why are these fields required to submit? Is there a way to just not require them and save the trouble of hiding them?
Related
Currently, the template is only generating the Submit button without any input fields.
Also, if I change fields = "all" to fields= ["email","name"] it tells me that these fields do not exist.
Model:
class NewsletterSubscriber(models.Model):
email = EmailField(required=True, label='Email')
name = CharField(required=True, label='Name')
Serializer:
class SubscribeForm(forms.ModelForm):
class Meta:
model = NewsletterSubscriber
fields = "__all__"
View:
def subscribe(request):
form = SubscribeForm()
if request.method == 'POST':
form = SubscribeForm(request.POST)
if form.is_valid():
form.save()
# redirect to a success page
return render(request, 'subscribe.html', {'subscribeForm': form})
Template:
<form action="{% url 'subscribe' %}" method="post">
{% csrf_token %}
{{ subscribeForm.as_p }}
<input type="submit" value="Subscribe">
</form>
Give this a try:
class NewsletterSubscriber(models.Model):
email = EmailField(required=True)
name = CharField(required=True)
class SubscribeForm(forms.ModelForm):
class Meta:
model = NewsletterSubscriber
fields = ['email', 'name']
labels = {'email': 'Email', 'name': 'Name'}
Instead of fields you could instead also go for exclude which would then obviously list the fields that you do not want to show up.
All these attributes you can put in the Meta class: Modelformfactory
Solved.
Wrong:
class NewsletterSubscriber(models.Model):
email = EmailField(required=True, label='Email')
name = CharField(required=True, label='Name')
Correct:
class NewsletterSubscriber(models.Model):
email = models.EmailField(required=True, label='Email')
name = models.CharField(required=True, label='Name')
Account is my AUTH_USER_MODEL and AccountDisplayInfo consists of all the additional display info of every account. So they can input and submit, and subsequently update their information. These are my codes, but I'm unsure why it isn't working. First of all, I am receiving this error:
DoesNotExist at /account/5/displayinfo/ AccountDisplayInfo matching query does not exist.
Secondly, the "update" function isn't working.
models.py
class Account(AbstractBaseUser):
email = models.EmailField(verbose_name="email", max_length=60, unique=True)
username = models.CharField(max_length=30, unique=True)
class AccountDisplayInfo(models.Model):
account = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
instagram = models.CharField(max_length=50, unique=True, blank=True, null=True) #instagram
.html
<form method="POST" enctype="multipart/form-data">{% csrf_token %}
{{ form.as_p }}
<div class="d-flex justify-content-center">
<button type="submit" class="btn btn-primary btn-sm col-lg-5">Update</button>
</div>
</form>
views.py
def display_information_view(request, *args, **kwargs):
user_id = kwargs.get("user_id")
account = Account.objects.get(pk=user_id)
context = {}
displayinfo = AccountDisplayInfo.objects.get(account=account)
if request.POST:
form = DisplayInformationForm(request.POST, request.FILES, instance=request.user)
if form.is_valid():
info = form.save(commit=False)
info.account = request.user
info.save()
messages.success(request, 'Your profile display information have been updated', extra_tags='editdisplayinfo')
return redirect("account:view", user_id=account.pk)
else:
form = DisplayInformationForm(request.POST, instance=request.user,
initial={
"instagram": displayinfo.instagram,
}
)
context['form'] = form
else:
form = DisplayInformationForm(
initial={
"instagram": displayinfo.instagram,
}
)
context['form'] = form
return render(request, "account/displayinfo.html", context)
forms.py
class DisplayInformationForm(forms.ModelForm):
class Meta:
model = AccountDisplayInfo
fields = ('instagram')
Also, would be great if you can advise on this::
If I have 2 tables. Table 1 and Table 2. Table 2 has a foreign key to table 1 but table 1 dont have a foreign key to table 2. How can I query table 2's data from table 1? Thanks
By default .get() will return a DoesNotExist exception if no object matches the query you executed and stop the code from running, so if you want to input it manually on the same page use filter instead:
displayinfo = AccountDisplayInfo.objects.filter(account=account).first()
Then in your template do something like this:
{% if displayinfo %}
... show display info...
{% else %}
<p> No info yet </p> <!-- (or show some form) -->
{% endif %}
To answer your other question:
You have to use the related_name or related models attribute to access the ForeignKey data or use the model name with the _set suffix, for example:
class Post(models.Model):
title = models.CharField(max_lenght=10)
class Comment(models.Model):
body = models.CharField(max_lenght=200)
post = models.ForeignKey(Post, on_delete=models.CASCADE, related_name='comments')
then you would get the Post and its comments:
post = Post.objects.get(pk=1)
comments = post.comments.all()
if you didn't have the related_name attribute in your model field you would do this instead:
comments = post.comment_set.all()
UPDATE
Maybe the issue is in your Form class, try removing the save method from it and instead do this in your view:
if request.POST:
form = DisplayInformationForm(request.POST, request.FILES, instance=request.user)
if form.is_valid():
info = form.save(commit=False)
info.account = request.user
messages.success(request, 'Your profile display information have been updated', extra_tags='editdisplayinfo')
info.save()
return redirect("account:view", user_id=account.pk)
Currently both of these print statements either log 'None' or just do not show at all. Even if I just print("hello') in the form.is_valid conditional I do not see it. Not sure what I have done wrong but the data was saving into the django admin but now it is not...
def create_user_account(request, *args, **kwargs):
form = UserAccountForm(request.POST or None)
print(request.POST.get('account_email'))
if form.is_valid():
print(form['account_email'].value())
form.save()
form = UserAccountForm()
context = {
'form': form
}
return render(request, 'registration/register_user.html', context)
and the html:
{% block content %}
<form action="{% url 'home' %}" method='POST'> {% csrf_token %}
{{form.as_p}}
<input type='submit' value='Submit'/>
</form>
{% endblock %}}
edit: i do get "POST / HTTP/1.1" 200 when I send the request which is weird. I don't see a 201 or 302 like I would expect.
forms.py:
class UserAccountForm(forms.ModelForm):
class Meta:
model = UserAccount
fields = [
'first_name',
'last_name',
'account_email',
'country'
]
class UserAccount(models.Model):
first_name = models.CharField(max_length=30, null=False)
last_name = models.CharField(max_length=30, null=False)
account_email = models.EmailField(max_length = 254, null=False)
country = models.CharField(choices=COUNTRY_CHOICES, null=False,
max_length=75)
activated = models.BooleanField(default=False)
CONTINUED:
So I dropped the database, migrated and the table appeared like normal. I tried to save data and the same problem? BUT I also tried to add information manually through the admin site and got this error:
OperationalError at /admin/profiles/useraccount/add/
no such table: main.auth_user__old
So I updated to Django 3.0.6 and this solved the problem - can now add manually but still cannot add through the form.
This was the solution. I don't know why it stopped working but it did. I feel I got finessed by indentation.
def register(request):
if request.method == 'POST':
form = UserAccountForm(request.POST)
if form.is_valid():
form.save()
return redirect('/')
else:
form = UserAccountForm()
context = {
'form': form
}
return render(request, 'registration/register_user.html', context)
I'm trying to add a placeholder to the username field in the UserCreationForm
but i can't understand how to do it.
I have already change the forms.py file in this way /lib/site-packages/django/contrib/auth/forms.py.
I add a placeholder to password1 and password2 fields and work like this:
class UserCreationForm(forms.ModelForm):
"""
A form that creates a user, with no privileges, from the given username and
password.
"""
error_messages = {
'password_mismatch': _("The two password fields didn't match."),
}
password1 = forms.CharField(
label=_("Password"),
strip=False,
widget=forms.PasswordInput(attrs={'placeholder':'Password'}),
help_text=password_validation.password_validators_help_text_html(),
)
password2 = forms.CharField(
label=_("Password confirmation"),
widget=forms.PasswordInput(attrs={'placeholder':'Confirmar Password'}),
strip=False,
help_text=_("Enter the same password as before, for verification."),
)
I can see probably the username field is coming from class meta:
class Meta:
model = User
fields = ("username",)
field_classes = {'username': UsernameField}
from this class but i'm not sure about that
class UsernameField(forms.CharField):
def to_python(self, value):
return unicodedata.normalize('NFKC', super().to_python(value))
I don't understand how add a placeholder to username field
this is my html
<form method="post" action=".">
{% csrf_token %}
{% for field in form %}
{{ field }}<br />
{% for error in field.errors %}
<p style="color: red">{{ error }}</p>
{% endfor %}
{% endfor %}
<input id="submit-signup-btn" type="submit" value="Iniciar"/>
</form>
After a while looking at this, i found a way to do it, this is the way i did it:
in the file /lib/site-packages/django/contrib/auth/forms.py, in class UserCreationForm, under class Meta there is a init function where you can see this line
self.fields[self._meta.model.USERNAME_FIELD].widget.attrs.update({'autofocus':
True})
i added the placeholder to the end of the line in the update method like this:
self.fields[self._meta.model.USERNAME_FIELD].widget.attrs.update({'autofocus': True,
'placeholder':'Nome Perfil'})
Based on #Gabriel costa answer, I came up with this solution:
class AccountCreationForm(UserCreationForm):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['username'].widget.attrs.update({'placeholder':_('Username')})
self.fields['email'].widget.attrs.update({'placeholder':_('Email')})
self.fields['password1'].widget.attrs.update({'placeholder':_('Password')})
self.fields['password2'].widget.attrs.update({'placeholder':_('Repeat password')})
Other solutions don't work in this scenario.
I had a similiar problem before, this is how i solved it :
class Meta:
......
widgets = {
'username': forms.fields.TextInput(attrs={'placeholder': 'random stuff'})
}
or internally , e.g
city = forms.CharField(widget=forms.TextInput(attrs={'placeholder':'text'}))
You can also define widgets in form's Meta class.
class Meta:
model = User
fields = ("username",)
field_classes = {'username': UsernameField}
widgets = {
'username': form.fields.TextInput(attrs={'placeholder': 'Your text for placeholder'})
}
I had to create a form from which some details go to default.auth.user model and some to my custom model so after searching from various sources I did this:
Django Version :1.7
model.py
class UserProfile(models.Model):
user = models.OneToOneField(User)
title_id = models.ForeignKey('Title')
mobile_number = models.CharField(max_length=10)
alternate_number = models.CharField(max_length=10)
date_of_birth = models.DateField()
profession_id = models.ForeignKey('Profession', null=True, blank=True)
house_no = models.CharField(max_length=100, blank=True, default='NA')
city_id = models.ForeignKey('City', null=True)
country_id = models.ForeignKey('Country', null=True)
state_id = models.ForeignKey('State', null=True)
locality_id = models.ForeignKey('Locality', null=True)
profile_picture_path = models.CharField(max_length=100, blank=True, default='NA')
forms.py:
class UserForm(forms.ModelForm):
password = forms.CharField(widget=forms.PasswordInput(attrs={'id': 'password'}))
email = forms.CharField(widget=forms.TextInput(attrs={'id': 'email_id'}))
username = forms.CharField(widget=forms.TextInput(attrs={'id': 'username'}))
first_name = forms.CharField(widget=forms.TextInput(attrs={'id': 'first_name'}))
last_name = forms.CharField(widget=forms.TextInput(attrs={'id': 'last_name'}))
class Meta:
model = User
fields = ('email', 'username', 'first_name', 'last_name', 'password')
class ExtraDetailsForm(UserForm):
confirm_password = forms.CharField(widget=forms.PasswordInput(attrs=
{'id':'confirm_password'}),max_length=32,
required=True,)
class Meta:
model = UserProfile
fields = ('email', 'username', 'title_id', 'first_name', 'last_name',
'password', 'confirm_password',
'date_of_birth', 'mobile_number', )
My view.py is :
def register(request):
# A boolean vakue for telling whether the registration was successful
registered = False
if request.method == 'POST':
user_form = UserForm(data=request.POST)
additional_details_form = ExtraDetailsForm(data=request.POST)
if user_form.is_valid() and additional_details_form.is_valid():
user = user_form.save()
user.set_password(user.password)
user.save()
additional_details = additional_details_form.save(commit=False)
additional_details.user = user
additional_details.save()
registered = True
else:
print(user_form.errors, additional_details_form.errors)
else:
user_form = UserForm
additional_details_form = ExtraDetailsForm
return render(request,
'users/register.html',
{'user_form' : user_form, 'additional_details_form': additional_details_form, 'registerered': registered})
regsiter.html:
{% if registerered %}
<p>Thank you for register. check ur email , entered email was</p>
{% else %}
<form action="/users/register/" method="post">{% csrf_token %}
{{ additional_details_form.as_p }}
<input type="submit" value="Register" />
</form>
{% endif %}
Now the good thing is that everything is working fine and details are being stored as they should be.
But the bad thing is I do not know whether it is a correct approach or not as I did not find any tutorial/resource where this approach is used?
This is correct approach and you do it almost right. Just couple notes:
if user_form.is_valid() and additional_details_form.is_valid():
In this line if user_form is invalid then validation for additional_details_form will not run. To always validate both change it to:
if all([user_form.is_valid(), additional_details_form.is_valid()]):
In else statement you set form class to *_form variables. It should be form instances instead:
user_form = UserForm()
additional_details_form = ExtraDetailsForm()
And it may be a good idea to wrap your save code into one transaction :-)
I would recommend that you use just one form here that contains all fields.
There is no benefit to using two forms, especially since one inherits the other, this is odd behaviour when you are then passing the POST data into each of them.
Consolidate the fields into a single form and then override the 'clean' method of the form to be able to check that the two password fields match.
You can create a single form to save data into one or many different models and this is especially useful in your case since you need to validate the data for these different models together.
Ok, firstly ExtraDetailsForm shouldn't inherit from UserForm because they are for different models. It should look something like this instead:
class UserForm(forms.ModelForm):
confirm_password = forms.CharField(widget=forms.PasswordInput(attrs=
{'id':'confirm_password'}),max_length=32,
required=True,)
class Meta:
model = User
fields = ('email', 'username', 'first_name', 'last_name', 'password',
'confirm_password')
class ExtraDetailsForm(forms.ModelForm):
class Meta:
model = UserProfile
fields = ('title_id', 'date_of_birth', 'mobile_number')
Then in your view:
from django.contrib.auth import login
from django.shortcuts import redirect, render
def register(request):
user_form = UserForm(data=request.POST or None)
profile_form = ExtraDetailsForm(data=request.POST or None)
if all([user_form.is_valid(), profile_form.is_valid()]):
user = user_form.save(commit=False)
user.set_password(user.password)
user.save()
profile = profile_form.save()
# probably at this point you want to login the new user:
login(request, user)
# it's good practice to do a redirect here, after a successful
# form post, eg to display success page, as this will
# prevent accidental re-posting data if user reloads the page
return redirect('registration_success')
else:
print(user_form.errors, profile_form.errors)
return render(
request,
'users/register.html',
{
'user_form' : user_form,
'profile_form' : profile_form,
}
)
def registration_success(request):
return render('registration_success.html')
Finally you need to output both forms in the template:
<form action="/users/register/" method="post">{% csrf_token %}
{{ user_form.as_p }}
{{ profile_form.as_p }}
<input type="submit" value="Register" />
</form>
and a new template registration_success.html:
<p>Thank you for registering. Check your email, entered email was: {{ request.user.email }}</p>