So i've been working on a project for a while, in which I use the Django default User Instance but with additional attributes, which are stored in a "Profile"-Model. So right now i have an assigned company, and an Profile-Image for the user in the Profile Model.
Now i have a detailview to edit the user's attributes (firstname, username, email, etc.). i used generic.DetailView for this View. But this is only working for the Attributes in the User-Model from Django. How can I also change Attributes in the Profile Model when editing the User?
This is my Edit-View:
class ProfileUpdateView(LoginRequiredMixin, UpdateView):
model = User
fields = ['username', 'email', 'first_name', 'last_name']
template_name = 'inventory/edit_forms/update_profile.html'
success_url = '/profile'
login_url = '/accounts/login'
redirect_field_name = 'redirect_to'
def form_valid(self, form):
profile = Profile.objects.get(user=self.request.user)
profile.img = self.request.POST.get('profile_img')
profile.save()
return super().form_valid(form)
def get_object(self):
return User.objects.get(pk = self.request.user.id)
This is my HTML:
<form class="edit_object" action="" method="post" enctype='multipart/form-data' class="form-group">
<div style="width: 20%; margin-bottom: 1rem;">
<label>Profile Image</label>
<input name="profile_img" type="file" accept="image/png, image/gif, image/jpeg">
</div>
{% csrf_token %}
{{form|crispy}}
<input type="submit" value="Submit" class="btn action_btn">
</form>
As you see i already tried using a external Input field and setting the image like that. But after submitting, the img attribute in the Profile-Model is just set null and I don't know why.
What did I miss?
if you want to go this way use request. FILES
Related
In my project, i have a template where i'm trying to put two forms for different use cases. I've never come across this problem before, so i don't really know where to go from here to use two forms in the same page.
At first i thought of creating another view to handle each form, but i think that this solution would create problems with the rendering of my templates, other than not being sustainable if i should have this problem again with another template.
After making some research, i found a solution but it works for class based views, but i'd like to avoid that since my view is already a function based view, and i would have to make a lot of changes in my code.
Would it be possible to solve this problem with a function based view? Every advice is appreciated
First field
class FirstForm(forms.ModelForm):
firstfield = forms.CharField()
secondfield = forms.CharField()
class Meta:
model = MyModel
fields = ("firstfield", "secondfield")
def save(self, commit=True):
send = super(FirstForm, self).save(commit=False)
if commit:
send.save()
return send**
Second Form
class SecondForm(forms.ModelForm):
firstfield = forms.FloatField()
secondfield = forms.Floatfield()
thirdfield = forms.CharField()
class Meta:
model = MyModelTwo
fields = ("firstfield", "secondfield", "thirdfield")
def save(self, commit=True):
send = super(SecondForm, self).save(commit=False)
if commit:
send.save()
return send
Template
<h3> First Form </h3>
<form method="post" novalidate>
{% csrf_token %}
{% include 'main/includes/bs4_form.html' with form=form %}
<button type="submit" class="btn btn-danger" style="background-color: red;">SUBMIT</button>
</form>
<h3> Second Form </h3>
<form method="post" novalidate>
{% csrf_token %}
{% include 'main/includes/bs4_form.html' with form=form %}
<button type="submit" class="btn btn-danger" style="background-color: red;">SUBMIT</button>
</form>
EDIT: my view:
def myview(request):
# if this is a POST request we need to process the form data
if request.method == 'POST':
# create a form instance and populate it with data from the request:
form = FirstForm(request.POST)
# check whether it's valid:
if form.is_valid():
# process the data in form.cleaned_data as required
# ...
# redirect to a new URL:
send = form.save()
send.save()
messages.success(request, f"Success")
# if a GET (or any other method) we'll create a blank form
else:
form = FirstForm()
return render(request,
"main/mytemplate.html",
context={"form":form})
This answer is a bit general because you haven't included your view function. You can add each of these forms to your view's context. Something like this:
views.py
...
from .forms import FirstForm, SecondForm
...
def some_view(request):
context = {
'first_form': FirstForm(request.POST or None),
'second_form': SecondForm(request.POST or None)
}
return render(request, "app/some_template.html", context)
I want to save changed values of ModelForm to database. I'm having problems even I follow this documentation if I'm right that it can be possible with initial values: Documentation- providing initial values
models.py:
class Settings(models.Model):
url = models.URLField(max_length=100, default='https://website.com')
maxCount = models.SmallIntegerField(default=30)
views.py:
def Settings(request):
settingsObj = Settings.objects.get(id=1)
form = SettingsForm(initial={'url': settingsObj.url, 'maxCount':settingsObj.maxCount}, instance=settingsObj)
if form.is_valid():
form.save()
forms.py:
class SettingsForm(forms.ModelForm):
class Meta:
model = Settings
fields = ['url', 'maxCount']
templates
<form class="form-horizontal" role="form" method="POST">{% csrf_token %}
<div class="form-group">
<div class="col-sm-offset-1 col-sm-6">
{{ form.as_p }}
</div>
</div>
<div class="form-group">
<div class="col-sm-offset-1 col-sm-6">
<button type="submit" class="btn btn-primary btn-lg btn-block">Accept</button>
</div>
</div>
</form>
Currently the form is showing the current values from database, but isn't saving changed data. form.is_valid() returns True, but form.save() seems to do nothing.
The initial argument and the instance argument in your call to SettingsForm() serve the exact same purpose, because you are using the fields of the instance individually in each field of initial.
The save() method is not working because you need to populate the form with data from request.POST.
This view should work:
def settings(request):
settingsObj = Settings.objects.get(id=1)
if request.POST:
form = SettingsForm(request.POST, instance=settingsObj)
if form.is_valid():
form.save()
else:
form = SettingsForm(instance=settingsObj)
context = { ..., 'form': form, ... }
return render(request, 'template-address', context)
How can I set myfile form field as Dropzone.js upload area?
forms.py
class TestForm(forms.Form):
title = forms.CharField(required=True)
myfile = FileField()
views.py
class ExampleView(FormView):
template_name = 'test.html'
form_class = TestForm
success_url = '/thanks/'
def form_valid(self, form):
print form.cleaned_data['title']
print form.cleaned_data['file']
return super(ExampleView, self).form_valid(form)
test.html
<form method="post" action="/test-form/">
<input type="text" name="title" id="id_title">
<input type="submit" value="Save">
</form>
Well, I just checked the dropzone website, and there are a few different ways.
One would be by configuring the paramName option: http://www.dropzonejs.com/#config-paramName
But it looks like you could just add the file field, e.g., <input type="file" name="myfile" />
You'll need to add class="dropzone" to the form.
here I am trying to allow users to make modifications to their user profile. There's a model called UserProfile that holds a one to one relationship to django user itself. Below is the code in views.py
#login_required
def edit_profile(request):
if request.method == 'POST':
profile = UserProfile.objects.get(user=request.user)
profile_form = UserProfileForm(data=request.POST, instance=profile)
if profile_form.is_valid():
profile = profile_form.save(commit=False)
profile.user = user
if 'picture' in request.FILES:
profile.picture = request.FILES['picture']
profile.save()
return HttpResponseRedirect("/me/login/")
else:
print user_form.errors, profile_form.errors
else:
user = request.user
profile = UserProfile.objects.get(user=user)
profile_form = UserProfileForm(initial={'website':profile.website,'address':profile.address, 'picture':profile.picture})
return render(request, 'member/edit_profile.html', {'profile_form': profile_form})
However, once the submit button is clicked from the template, I got an error saying that a password is needed.
[27/Apr/2015 14:25:07] "GET /me/edit_profile2/ HTTP/1.1" 200 5080
<ul class="errorlist"><li>username<ul class="errorlist"><li>This field is required.</li></ul></li><li>password<ul class="errorlist"><li>This field is required.</li></ul></li></ul>
[27/Apr/2015 14:25:16] "POST /me/edit_profile/ HTTP/1.1" 200 6384
from the code, I thought that the UserProfile is already bound to a specific user already, and I am only allowing users to make changes on the UserProfile model without touching django's auth User model. I wonder why the username and password is still needed in this case. Would it be possible to allow editing on user profile without users' password?
Here is the UserProfile extended from the User model
class UserProfile(models.Model):
# link user profile to the user models
user = models.OneToOneField(User)
website = models.URLField(blank=True)
picture = models.ImageField(upload_to='avatar', blank=True)
address = models.TextField(blank=True)
#property
def stats(self):
"""get statistics for this profile"""
from tumboon.models import Donation
return Donation.statistics.for_user(self)
#property
def amount_donated(self):
__doc__ = """get the total amount donated """
return self.stats['total_amount_donated']
# Override the __unicode__ to return something meaningful
def __unicode__(self):
return self.user.username
class UserForm(forms.ModelForm):
"""Form for User Registration"""
class Meta:
model = User
fields = ('username', 'email', 'password', 'first_name', 'last_name')
widgets = {
'username' : forms.TextInput(attrs = {'placeholder': 'Username'}),
'email' : forms.TextInput(attrs = {'placeholder': 'Email'}),
'password' : forms.TextInput(attrs = {'placeholder': 'Password'}),
}
class UserProfileForm(forms.ModelForm):
"""Form for UserProfile"""
class Meta:
model = UserProfile
fields = ('website', 'picture', 'address')
And the UserProfileForm on the template is here:
{% if user.is_authenticated %}
<form id="user_profile" class="form-horizontal" method="POST" enctype="multipart/form-data" action="/me/edit_profile/">
{% csrf_token %}
<h3>User Info</h3>
<hr />
<div class="form-group">
<label class="col-xs-2" for="{{ user_form.picture.id_for_label }}">Picture: </label>
<div class="col-xs-10">
{{profile_form.picture|add_class:"form-control"}}
</div>
</div>
<div class="form-group">
<label class="col-xs-2" for="{{ user_form.website.id_for_label }}">Website: </label>
<div class="col-xs-10">
{{profile_form.website|add_class:"form-control"}}
</div>
</div>
<div class="form-group">
<label class="col-xs-2" for="{{ user_form.address.id_for_label }}">Address: </label>
<div class="col-xs-10">
{{profile_form.address|add_class:"form-control"}}</li>
</div>
</div>
<input class="btn btn-default" type="submit" name="save_button" value="Save Profile">
</form>
{% endif %}
Your UserForm class is extending Model.Form class which displays the Password field as required, hence the problem. Use instead the UserChangeForm:
from django.contrib.auth.forms import UserChangeForm
class UserForm(UserChangeForm):
class Meta:
model = User
fields = ('username', 'email', 'first_name', 'last_name', 'is_super_admin')
This form handles password as it should.
I am using Django Userena for the first time.So can not able to customize the appearance of the change password form,as we know that userena used the default change password form from django.contrib.auth.forms (if i am not wrong).Now this is becoming tough for me to customize the appearance of the change password form template cause in the change password template, each and every field is rendered as {{ form.as_p }} like that
<form action = "" method="post" role = "form">
<fieldset>
<legend>{% trans "Change Password" %}</legend>
{% csrf_token %}
{{ form.as_p }}
</fieldset>
<input type="submit" value="{% trans "Change password" %}" class="btn btn-success" />
</form>
in mention,i have already been able to format the appearance of other forms provided by userena.for example i have changed the appearance of the Edit Profile form by adding css classes in the forms.py like that
class EditProfileForm(forms.ModelForm):
""" Base form used for fields that are always required """
first_name = forms.CharField(label=_(u'First name'),
max_length=30,
widget=forms.TextInput(attrs={'class' : 'form-control'}),
required=False)
last_name = forms.CharField(label=_(u'Last name'),
max_length=30,
widget=forms.TextInput(attrs={'class' : 'form-control'}),
required=False)
background = forms.CharField(label=(u'Background'),
max_length=500,
widget=forms.Textarea(attrs={'class' : 'form-control'}),
required=True)
and worked, change password form has been rendered from django.contrib.auth.forms,so i don't know how to add css classes in each field of that that file as it is a core file of Django.May be there alternative way to do this ,but i am inexperience in django and also the django userena,i don't know how do this.
It's too late, but for the new visitors,
you can create a new form in your forms.py as
# forms.py
from django.contrib.auth.forms import PasswordChangeForm
...
class MyPasswordChangeForm(PasswordChangeForm):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields["old_password"].widget = forms.PasswordInput(attrs={"class": "form-control"})
self.fields["new_password1"].widget = forms.PasswordInput(attrs={"class": "form-control"})
self.fields["new_password2"].widget = forms.PasswordInput(attrs={"class": "form-control"})
# other customization
and in your views.py you could use PasswordChangeView with your form as
# views.py
...
from django.contrib.auth.views import PasswordChangeView
from .forms import MyPasswordChangeForm
...
class ChangePasswordView(PasswordChangeView):
form_class = MyPasswordChangeForm
template_name = "path/to/your/template.html"
That's all.
You actually need to override the userena view altogether because it passes its own form in the view
urls.py:
# Change password
url(r'^(?P<username>[\#\.\w-]+)/password/$',
accounts.views.my_own_password_change_view,
name='userena_password_change'),
In your views.py:
#secure_required
#permission_required_or_403('change_user', (get_user_model(), 'username', 'username'))
def my_own_password_change_view(request, username, template_name='userena/password_form.html',
pass_form=YourPasswordChangeForm, success_url=None, extra_context=None):
""" Change password of user.
This view is almost a mirror of the view supplied in
:func:`contrib.auth.views.password_change`, with the minor change that in
this view we also use the username to change the password. This was needed
to keep our URLs logical (and REST) across the entire application. And
that in a later stadium administrators can also change the users password
through the web application itself.
:param username:
String supplying the username of the user who's password is about to be
changed.
:param template_name:
String of the name of the template that is used to display the password
change form. Defaults to ``userena/password_form.html``.
:param pass_form:
Form used to change password. Default is the form supplied by Django
itself named ``PasswordChangeForm``.
:param success_url:
Named URL that is passed onto a :func:`reverse` function with
``username`` of the active user. Defaults to the
``userena_password_complete`` URL.
:param extra_context:
Dictionary of extra variables that are passed on to the template. The
``form`` key is always used by the form supplied by ``pass_form``.
**Context**
``form``
Form used to change the password.
"""
user = get_object_or_404(get_user_model(),
username__iexact=username)
form = pass_form(user=user)
if request.method == "POST":
form = pass_form(user=user, data=request.POST)
if form.is_valid():
form.save()
# Send a signal that the password has changed
userena_signals.password_complete.send(sender=None,
user=user)
if success_url: redirect_to = success_url
else: redirect_to = reverse('userena_password_change_complete',
kwargs={'username': user.username})
return redirect(redirect_to)
if not extra_context: extra_context = dict()
extra_context['form'] = form
extra_context['profile'] = get_user_profile(user=user)
return ExtraContextTemplateView.as_view(template_name=template_name,
extra_context=extra_context)(request)
And finally
class YourPasswordChangeForm(forms.ModelForm):
""" Base form used for fields that are always required """
first_name = forms.CharField(label=_(u'First name'),
max_length=30,
widget=forms.TextInput(attrs={'class' : 'form-control'}),
required=False)
last_name = forms.CharField(label=_(u'Last name'),
max_length=30,
widget=forms.TextInput(attrs={'class' : 'form-control'}),
required=False)
background = forms.CharField(label=(u'Background'),
max_length=500,
widget=forms.Textarea(attrs={'class' : 'form-control'}),
required=True)
do even more customization on the html template
<form action="" method="post" id="password_change_form">
{% csrf_token %}
{% for field in form %}
<div class="profile-input w33">
<div class="profile-label" for="{{ field.name }}">{{ field.label }}</div>
{{ field }}
{{ field.errors }}
</div>
{% endfor %}
<div class="profile-input w33">
<input type="submit" class="input updatebtn" value="{% trans "UPDATE" %}"/>
</div>
</form>
If you are going to use Bootstrap and jQuery you can also customize all templates in your userena base file with jQuery.
In my case it saved me a lot of messy code in multiple files across the project.
Just change the desired parts with jQuery or pure JS and CSS for example:
$( "input" ).addClass( "form-control" );