I am only trying to test the default user profile update through UserChangeForm. Just the email field. So below are the code snippet.
views.py
#login_required(login_url="/login/")
def editUserProfile(request):
if request.method == "POST":
form = UserProfileUpdateForm(request.POST, instance=request.user)
if form.is_valid():
form = UserProfileUpdateForm(request.POST)
form.save()
return redirect('thank_you')
else:
messages.error(request, f'Please correct the error below.')
else:
form = UserProfileUpdateForm(instance=request.user)
return render(request, "authenticate\\editProfilePage.html", {'form': form})
forms.py
class UserProfileUpdateForm(UserChangeForm):
email = forms.EmailField()
class Meta:
model = User
fields = ('email', )
HTML
<div class="container h-100">
<div class="d-flex justify-content-center h-100">
<div class="user_card">
<div class="d-flex justify-content-center">
<h3 id="form-title">Update Profile</h3>
</div>
<div class="d-flex justify-content-center form_container">
<form method="POST" action="{% url 'editUserProfile' %}">
{% csrf_token %}
<div class="input-group mb-2">
<div class="input-group-append">
<span class="input-group-text"><i class="fas fa-envelope-square"></i></span>
</div>
{{form.email}}
</div>
<div class="d-flex justify-content-center mt-3 login_container">
<input class="btn login_btn" type="update" value="update">
</div>
</form>
</div>
{{form.errors}}
<script>
/* Because i didnt set placeholder values in forms.py they will be set here using vanilla Javascript
//We start indexing at one because CSRF_token is considered and input field
*/
//Query All input fields
var form_fields = document.getElementsByTagName('input')
form_fields[4].placeholder='email';
for (var field in form_fields){
form_fields[field].className += ' form-control'
}
</script>
</body>
In the user profile page, I could see the update button, and when I click on it I am redirected to the edit profile page, and I am also able to see the old email address mentioed in the email field. So far so good.
However, when I replace the old email with new one and click on the "update" button nothing happens. No error, no redirection. Nothing. The page remains there.
There were sytax errors in my HTML code, so I re-wrote the entire HTML again. That resolved the issue. Thank you
Related
Using Bootstrap5, Django and Crispy Forms to create a calculator application.
Application works as expected however I want to make changes to the appearance of the forms.
I've already removed the required field asterisk by simply adding the following to the CSS file:
.asteriskField {
display: none;
}
But now I want to place the label inside the field which requires a different approach that I'm currently unable to understand despite extensive research.
Having used Inspect in Chrome I can see that the label is of course a separate element but one housed within the field to achieve the given effect.
Current Form
Desired result
Settings.py (excerpt)
CRISPY_TEMPLATE_PACK = 'bootstrap5'
Forms.py (excerpt)
class InvestmentForm(forms.Form):
starting_amount = forms.ChoiceField(choices=STARTING_AMOUNT)
deposit_amount = forms.FloatField()
trade_in_value = forms.FloatField()
number_of_years = forms.FloatField()
credit_score = forms.ChoiceField(choices=CREDIT_SCORE)
state = forms.ChoiceField(
label='State',
choices=[(zipcode, zipcode) for zipcode in StateTax.objects.values_list('zipcode', flat=True).distinct()]
)
Index.html (excerpt)
<div class="row justify-content-center">
<h5 class="text-center"> Enter details below to see your estimate</h5>
</div>
<div class="col-md-8 mx-auto">
<form action="" method="POST">
{% csrf_token %}
{{ form|crispy }}
<br>
<button class="btn btn-primary" type="submit">Calculate</button>
</form>
<br>
</div>
</div>
You could use crispy-bootstrap5 which introduces floating labels. Just follow the installation guidelines and add a floating input field in your crispy Layout like:
FloatingField("first_name"),
I have the following views:
def device_port(request):
devices = Device.objects.all()
if request.method == "POST":
selected=request.POST.get('device')
devices = Device.objects.get(pk=selected)
tablename = 'dev_interface_'+selected
print("tablename: " +tablename)
cursor=connection.cursor()
cursor.execute(f"SELECT interface FROM {tablename} WHERE id >=2")
righttable = cursor.fetchall()
return redirect('/device/port/selected',{'devices':devices, 'selected': selected, 'righttable':righttable} )
return render(request, 'interface/device_port.html',{'devices':devices})
def device_port_selected(request, pk):
if request.method == "POST":
job = JobForm(request.POST)
device = devices.hostname
print(devices)
#job.associateddevice = devices.hostname
try:
selection=request.POST.get('portrange')
except:
selection = ""
messages.warning(request, "Please select the ports")
print(selection)
#job.associatedinterface = selection
return render(request, 'interface/device/port/selected/'+device+'.html',{'devices':devices, 'righttable':righttable} )
return render(request, 'interface/device_port_selected.html',{'devices':devices, 'selected': selected, 'righttable':righttable} )
urls.py
urlpatterns = [
path('', views.home, name='interface-home'),
path('device/', DeviceListView.as_view(), name='interface-device'),
path('device_edit/<int:pk>/', views.device_edit, name='device-edit'),
path('device_delete/<int:pk>/', views.device_delete, name = 'device-delete'),
path('device_add/', views.device_add, name='device-add'),
path('device/port/', views.device_port, name='device-port'),
path('device/port/selected/', views.device_port_selected, name='device-port-selected'),
path('device/routeport/', views.device_routeport, name='device-routeport'),
path('interface/', views.interface_list, name='interface-list')
]
device_port.html
<form method="POST">
<div class="form-row align-items-center">
<div class="col-md-5 my-1">
{% csrf_token %}
<label for="Hostname">Hostname</label>
<div class="input-group">
<select id = "list" class="custom-select mr-sm-2" onchange="getSelectValue();">
<option selected>Select</option>
{% for device in devices %}
<option value={{device.id}}>{{device.hostname}}</option>
{%endfor%}
</select>
<div class="input-group-append">
<button class="btn btn-outline-secondary" type="submit">Go</button>
</div>
</div>
</div>
</div>
<input type ="text" name="device" id= "txtvalues" style="display:none">
</form>
So there are 2 page I am dealing with over here (/device/port and /device/port/selected). In this first page /device/port, user is required to pick a value from the drop down box and press the button Go. From here, it is intended to go to the next page which is /device/port/selected with the selected value in the first page pass to the next page.
But with the following code, I receive the error of
device_port_selected() missing 1 required positional argument: 'pk'
when moving from the first page to the next page.
You can't pass a context dictionary to a redirect. The second argument should be the URL arguments, not a context. So change the following line:
return redirect('/device/port/selected',{'devices':devices, 'selected': selected, 'righttable':righttable} )
to
return redirect('device-port-selected', pk=selected)
Note that it is better to use the name of the URL (i.e. device-port-selected) instead of the whole path as you can change paths in the future without affecting the rest of your code.
I'm making an app to save mileage of a truck per state. I've already passed required data to my view, and then I thought to change my urls to more logical. And after that I faced a problem.
I don't know what should be instead of "unit.unit_number", and it is required, in my html file for it to work correctly. I didn't find anything that could explain how to deal with it.
If I try to access mywebsite.com/core/units/1/locations/add/ I get next error message:
"NoReverseMatch at /core/units/1/locations/add/"
But if I put just a number (1 for example) instead of "unit.unit_number" it loads the page normally, but I get an error after trying to post the data:
"TypeError at /core/units/1/locations/add/
_reverse_with_prefix() argument after * must be an iterable, not int"
<form action="{% url 'Core:unit_add_location' unit.unit_number %}" method="POST" enctype="multipart/form-data">
{% csrf_token %}
<div class="container">
<div class="inner-container border-full">
<button type="button" class="button button-normal" onclick="AddRow()">Add</button>
<input type="submit" class="button button-submit" name="count-ifta" value="Save">
<div class="inner-container border-top-bottom">
<table id="myTable" name="state-miles-data">
<thead>
<th class="text-blue">State</th>
<th class="text-blue">Miles</th>
</thead>
</table>
<br>
</div>
</form>
<button type="button" class="button button-normal" onclick="AddRow()">Add</button>
</div>
</div>
def unit_data(request, unit_number):
return HttpResponse(unit_number)
def unit_add_location(request, unit_number):
if "GET" == request.method:
return render(request, 'Core/add_location.html')
elif "POST" == request.method:
states_request = request.POST.getlist('states')
miles_request = request.POST.getlist('miles')
return HttpResponseRedirect(reverse('Core:unit_data', args=(unit_number)))
urlpatterns = [
path('units/', views.units_all, name = 'units_all'),
path('units/<int:unit_number>/', views.unit_data, name = 'unit'),
path('units/<int:unit_number>/locations/', views.unit_locations, name = 'unit_locations'),
path('units/<int:unit_number>/locations/add/', views.unit_add_location, name = 'unit_add_location'),
path('units/<int:unit_number>/locations/<int:report_id>', views.unit_location, name = 'unit_location'),
]
So the thing I want to make is post the data and redirect to url "mywebsite.com/units/1/locations/" that is processed by the view "unit_locations"
To the second part about the iterable: (reverse('Core:unit_data', args=(unit_number))) <<< you want (unit_number,) which is a tuple not an int. Spot the , it is important.
I have a "GET" / "POST" split on my html page courtesy of a python flask script:
def home(name=None):
if request.method == "GET":
return render_template('home.html', name=name)
if request.method == "POST":
files = request.form["file[]"]
do things with the files
On my home.html page, I have added in some bootstrap code to make everything pretty, but the flask module is now not taking in the form data properly. Here's my html code:
<form id="uploadbanner" enctype="multipart/form-data" method="post" action="{{ url_for('home') }}">
<div class="form-group">
<div class="container">
<div class="page-header">
<br>
<h1>Title</h1>
<br>
<br>
</div>
<div class="row">
<div class="col-lg-6 col-md-6 col-s-6 col-xs-12">
<h4>File Control</h4>
<div class="jumbotron">
And my form data is here:
<input type="file" name="file[]" id="inputnameid" multiple=""/>
</div>
</div>
Second Column
</div>
New Row, etc etc
Each new row has buttons and inputs that I want to match with the form data using the name field.
I know I'm not structuring this properly, but I can't seem to find any info on how to mesh bootstrap and forms.
I think in order to get the files, you should use request.files["file[]"] instead of request.form["file[]"] .
I was figuring how to reuse the same registration form and view on multiple templates and pages, and in different formats. E.g. on the page, in a modal etc. I am however some trouble in figuring out the best practice for solving this problem. One thing I am actively trying to avoid is repeating myself, but I can't seem to find a solution that is satisfying enough.
At the moment I have one central view that handles user registrations that looks like this. At the moment it can only handle to output one form on the signup_form template, but I want to extend that to the index page and be able to be outputted in a modal as well.
Views.py
def signup(request):
template_name = 'core/authentication/signup_form.html'
custom_error_list = []
if request.method == "POST":
form = SignUpForm(request.POST)
if form.is_valid():
#Check for duplciate email
email = form.cleaned_data.get('email')
username = form.cleaned_data.get('username')
if email and User.objects.filter(email=email).exclude(username=username).exists():
custom_error_list.append("A user with that email already exists")
else:
user = form.save(commit=False)
user.is_active = False
user.save()
current_site = get_current_site(request)
subject = 'Activate your StockPy Account'
sender = '' #Insert sender address here
message = render_to_string('core/authentication/account_activation_email.html', {
'user': user,
'domain': current_site.domain,
'uid': urlsafe_base64_encode(force_bytes(user.pk)),
'token': account_activation_token.make_token(user)
})
user.email_user(subject, message)
return redirect('authentication:account_activation_sent')
else:
form = SignUpForm()
return render(request, template_name, {'form': form, 'custom_error_list': custom_error_list})
#Activate the user as he/she clicks the email verification link which lead to tihs view
def activate(request, uidb64, token):
try:
#Using a [:1] is ad-hoc solution to get rid of the starting 'b' symbol
uid = force_text(urlsafe_base64_decode(uidb64[1:]))
user = User.objects.get(pk=uid)
except (TypeError, ValueError, OverflowError, User.DoesNotExist):
user = None
if user is not None and account_activation_token.check_token(user, token):
user.is_active = True
user.profile.email_confirmed = True
user.save()
login(request, user)
return redirect(template_name)
else:
return render(request, 'core/authentication/account_activation_invalid.html')
forms.py
from django.contrib.auth.models import User
from django.contrib.auth.forms import UserCreationForm
from django import forms
class LoginForm(forms.ModelForm):
password = forms.CharField(widget=forms.PasswordInput)
class Meta:
model = User
fields = ['email','password']
class SignUpForm(UserCreationForm):
email = forms.EmailField(max_length=254, widget=forms.TextInput(attrs={'placeholder': 'Email...', 'class' : 'form-control', 'pattern' : '[a-z0-9._%+-]+#[a-z0-9.-]+\.[a-z]{2,3}$'}))
class Meta:
model = User
fields = ['email', 'username', 'password1', 'password2']
def __init__(self, *args, **kwargs):
super(SignUpForm, self).__init__(*args, **kwargs)
self.fields['username'].widget = forms.TextInput(attrs={
'class': 'form-control',
'placeholder': 'Username...',
})
self.fields['password1'].widget = forms.TextInput(attrs={
'class': 'form-control',
'placeholder': 'Password...',
'type': 'password',
})
self.fields['password2'].widget = forms.TextInput(attrs={
'class': 'form-control',
'placeholder': 'Password again...',
'type': 'password',
})
My signup form currently looks like this.
signup_form.html
{% extends 'base.html' %}
{% load static %}
<!-- End Navbar -->
{% block page-header %}
<div class="section section-signup" style="background-image: url({% static 'core/assets/img/bg8.jpg' %}); background-size: cover; background-position: top center; min-height: 700px;">
<div class="container">
<div class="row">
<div class="card card-signup" data-background-color="orange">
<form class="form" method="POST" action="">
{% csrf_token %}
<div class="header text-center">
<h4 class="title title-up">Sign Up</h4>
<div class="social-line">
<a href="#twitter" class="btn btn-neutral btn-twitter btn-icon btn btn-round">
<i class="fa fa-twitter"></i>
</a>
<a href="#facebook" class="btn btn-neutral btn-facebook btn-icon btn-lg btn-round">
<i class="fa fa-facebook-square"></i>
</a>
<a href="#google" class="btn btn-neutral btn-google btn-icon btn-round">
<i class="fa fa-google-plus"></i>
</a>
</div>
</div>
<div class="card-body">
<!-- Output error messages -->
{% for field in form %}
<div style="color:red; list-decorations:none;" class="text-center">
{{ field.errors.as_text }}
</div>
{% endfor %}
{% for error in custom_error_list %}
<div style="color:red;" class="text-center">
* {{ error }}
</div>
{% endfor %}
<!-- Output all fields -->
{% for field in form %}
<div class="input-group form-group-no-border">
<span class="input-group-addon">
<i class="now-ui-icons
{% if field.name == 'email' %} ui-1_email-85{% endif %}
{% if field.name == 'username' %} users_circle-08{% endif %}
{% if field.name == 'password1' %} ui-1_lock-circle-open{% endif %}
{% if field.name == 'password2' %} ui-1_lock-circle-open{% endif %}
"></i>
</span>
{{ field }}
<!-- Give input box red border if data is not valid -->
{% if field.errors %}
<script>
var element = document.getElementById("{{ field.id_for_label }}");
element.classList.add("form-control-danger");
</script>
{% endif %}
</div>
{% endfor %}
<div class="text-center">
Already registered? Log in here
</div>
<!-- If you want to add a checkbox to this form, uncomment this code -->
<!-- <div class="checkbox">
<input id="checkboxSignup" type="checkbox">
<label for="checkboxSignup">
Unchecked
</label>
</div> -->
</div>
<div class="footer text-center">
<button type="submit" class="btn btn-neutral btn-round btn-lg">Get Started</button>
</div>
</form>
</div>
</div>
</div>
</div>
{% endblock page-header %}
And a small example snippet of my index.html of how I want to implement it ish.
index.html
<div class="main">
<div class="section section-about-us">
<div class="container">
<div class="col-md-8 ml-auto mr-auto text-center">
{{ form }}
</div>
</div>
</div>
</div>
I have really tried to find a smooth way of doing this, but without result unfortunately.
It seems as though you already know how to implement the same form in multiple templates, but you've having trouble avoiding repetition in your code. To that end, here are some suggestions for reducing the amount of repetition you'll encounter when duplicating this form across multiple pages:
Validate data within your form rather than your view. Currently, you are checking for duplicate e-mail addresses within views.py. If you duplicated your form, you'd have to re-write that code all over again. Instead, why not move it into forms.py in a custom cleaning method (see Django docs on custom form cleaning).
Write functions for actions that will be repeated. For example, currently, you are sending an activation e-mail to your user within views.py. It makes more sense to write a function within your user/models.py called something like send_activation_email(). Whenever you want to send an activation e-mail to a user, you can then call user.send_activation_email() rather than duplicating your entire block of activation e-mail code (see Django docs on model methods).
Use inclusion tags to avoid repetition in your templates. If there's a block of code that you find yourself repeating in your templates, you can use Django's inclusion tags to include a snippet of HTML across multiple templates. This allows you to serve the same form in multiple locations without re-writing your code. If you want to style your form differently across multiple pages, you could wrap it in DIVs with different IDs and use CSS to style the form differently depending on which DIV it's wrapped in. (see Django docs on inclusion tags)