Overriding django-allauth views to use with modals - django

I am trying to handle login/registration functionality in a modal. I got successful login/registration working by importing the LoginForm and RegistrationForm into my modal view and then posting to the appropriate allauth URLs. The desired behavior is to have forms with errors rendered asynchronously in the modal.
I have not been able to get forms with errors (email doesn't exist when trying to login, passwords don't match when registering etc.) to render as an html partial in a modal with the errors. I'm not too sure where to start when trying to add this functionality into my own view/how to piggyback on the allauth views and change their functionality.
Adding the below to my views.py and url.py I've managed to get the allauth default template to load when the form is invalid (e.g. email field does not contain a valid email) but have not been able to get my template to load.
From views.py:
class LoginViewSnippet(LoginView):
success_url = reverse_lazy('home')
template_name = 'user_app/partials/loginmodal.html'
def get_context_data(self, **kwargs):
print('here1')
context = super(LoginView,self).get_context_data(**kwargs)
return context
def form_invalid(self, form):
print('here')
error_msg = 'error'
return HttpResponse(error_msg, status=400)
login = LoginViewSnippet.as_view()
From urls.py:
path('accounts/login',user_app_views.login, name='account_login'),
From user_app/partials/loginmodal.html:
...
<div class="modal-body">
<form id="loginform" method="POST" action="{% url 'account_login' %}" autocomplete="off">
{% csrf_token %}
{% for field in loginform %}
<div class="form-group mb-3">
{{ field.errors }}
{{ field | as_crispy_field }}
</div>
{% endfor %}
</form>
</div>
<div class="mx-auto">
<button form="loginform"type="submit" class="btn btn-success" hx-post="{% url 'account_login' %}" hx-target="#modals-here">Login</button>
<button type="button" class="btn btn-secondary" onclick="closeModal()">Close</button>
</div>
...

Related

Django - Passing an extra argument from template to urls.py to class-based view

Basically, I would like to get some advice about how to transition from a form page back to a previous page when the form is submitted.
There are other posts on similar situations but none seem to show how to implement this using a class-based view, so any advice here would be most appreciated.
So far I have come up with the approach below. Here, I am trying to pass a parameter holding the URL of the previous page, from the template for the previous page to urls.py and then onto the view. However, I don't think I am using the correct syntax in urls.py as the value of the previous_url parameter in the view is simply "previous_url".
Link in the template for the table page
Edit
URL
path('entry/<int:pk>/edit/', EntryUpdateView.as_view(previous_url="previous_url"), name='entry_update'),
View
class EntryUpdateView(LoginRequiredMixin, UpdateView):
model = Entry
template_name = 'entry_update.html'
fields = ('source', 'target', 'glossary', 'notes')
previous_url = ""
def form_valid(self, form):
obj = form.save(commit=False)
obj.updated_by = self.request.user
obj.save()
return HttpResponseRedirect(self.previous_url)
Update form
<form method="POST" novalidate>
{% csrf_token %}
<div class="mb-3">
{{ form.source|as_crispy_field }}
</div>
<div class="mb-3">
{{ form.target|as_crispy_field }}
</div>
<div class="mb-3">
{{ form.glossary|as_crispy_field }}
</div>
<div class="mb-3">
{{ form.notes|as_crispy_field }}
</div>
<div class="text-center mt-3">
<button type="submit" class="btn btn-outline-primary mx-2">Update</button>
</div>
</form>
Error
Page not found (404)
Request Method: GET
Request URL: http://localhost:8000/entry/5131/edit/previous_url
If this helps anyone, I was able to get the value of previous_url within form_valid() using self.request.GET.get('previous_url').
def form_valid(self, form):
obj = form.save(commit=False)
obj.updated_by = self.request.user
obj.save()
previous_url = self.request.GET.get('previous_url')
return HttpResponseRedirect(previous_url)

How to Add Subscribe option in a Django Website

I am trying to add a subscribe to newsletter option on a django website. When a visitor enters
a valid email address it will be stored in the database. The subscription form is part of the base.html template.
All other templates of the website extend this template. I wish to implement this in a DRY way.
This is how I am trying to do it :
forms.py :
from dataclasses import fields
from django import forms
from . models import Subscribers, MailMessage
class SubcribersForm(forms.ModelForm):
class Meta:
model = Subscribers
fields = ['email', ]
views.py :
def base(request):
if request.method == 'POST':
form = SubcribersForm(request.POST)
if form.is_valid():
form.save()
return redirect('/')
else:
form = SubcribersForm()
context = {'form': form}
return render(request, 'base.html', context)
The template: base.html
<form method = "POST" class="signup-form form-inline justify-content-center pt-3">
{% csrf_token %}
<div class="form-group">
<label class="sr-only" for="semail">{{context}}</label>
<input type="email" id="semail" name="semail1" class="form-control mr-md-1 semail" placeholder="Enter email">
</div>
<button type="submit" class="btn btn-primary">Subscribe</button>
</form>
models.py :
class Subscribers(models.Model):
email = models.EmailField(null=True)
date = models.DateTimeField(auto_now_add=True)
def __str__self(self):
return self.email
In the backend, I can see that the Subscribers table has been created. However, when I enter any email address from the home
page and click subscribe button it does not store it in the database. What could be the issue here?
It could be that you have no action declared in your form. Assuming you have a url like this:
path('add-subscriber/', base, name='base'),
...your form would need a way to call it upon submit, like this:
<form method = "POST" action="{% url 'base' %}" class="signup-form form-inline justify-content-center pt-3">
{% csrf_token %}
<div class="form-group">
<label class="sr-only" for="semail">{{context}}</label>
<input type="email" id="semail" name="semail1" class="form-control mr-md-1 semail" placeholder="Enter email">
</div>
<button type="submit" class="btn btn-primary">Subscribe</button>
</form>

How do you render form from views in template without model?

I have a problem with my Django Code.I'm trying to render a form from views to template and i'm just seeing the submit button. I noticed that we can use forms dynamically by introducing it like this {{ form }}, but when I use it, I just see the "submit" button on the page(Sorry I don't know how to upload a local image here). I join my four files: views.py, home.html, forms.py and urls.py
Thank you in advance
home.html
<form method="POST" novalidate action="/config">
{% csrf_token %}
<fieldset>
<legend class="border-bottom mb-4">Home</legend>
{{ form.as_p }}
</fieldset>
<div class="form-group">
<button class="btn btn-outline-info" type="submit">Sign Up</button>
</div>
</form>
views.py
def inputHome(request):
form = InputHomeForm()
if request.method == 'POST':
form = InputHomeForm(request.POST)
if form.is_valid():
mk = form.cleaned_data['mk']
return HttpResponseRedirect('blog-config')
else:
form = InputHomeForm()
return render(request, 'blog/home.html', {'form': form})
forms.py
class InputHomeForm(forms.Form):
mk = forms.CharField(widget=forms.TextInput(attrs={'class': 'special'}))
urls.py
urlpatterns = [
path('home/', blog_views.home, name='blog-home'),
]
I don't have an error message so i don't have an idea of the problem.
You are missing form tag in html.
HTML should be,
<form method='post'>
{% csrf_token %}
<fieldset>
<legend class="border-bottom mb-4">Home</legend>
{{ form.as_p }}
</fieldset>
<div class="form-group">
<button class="btn btn-outline-info" type="submit">Sign Up</button>
</div>
</form>
Slightly unrelated (cf Nishant's answer for the main issue), but here:
if request.method == 'POST':
form = InputHomeForm(request.POST)
if form.is_valid():
mk = form.cleaned_data['mk']
return HttpResponseRedirect('blog-config')
else:
form = InputHomeForm()
In the else clause, you're replacing the bound invalid form (which carries the validation errors) with an unbound form, so you'll never get the error messages. Just remove the whole else clause.

Multiple POST request is not working after submit

I am trying to use 2 post method in a single page one is for login and other one is for contact us
login is working fine but after submitting contact us the content of login and contact us page is gone
I tried to pass various type of dictionary but still, it's not working
app/views.py
from django.http import HttpResponse, HttpResponseRedirect
from django.shortcuts import render
from homePage.forms import SignInForm,DropUsaMessage
# Create your views here.
def homePage(request):
if request.method == 'POST' and 'SignIn' in request.POST:
sign_in_detail = SignInForm(request.POST)
if sign_in_detail.is_valid():
return render(request, "index2.html",{})
elif request.method == 'POST' and 'SendMessage' in request.POST:
message_detail = DropUsaMessage(request.POST)
if message_detail.is_valid():
return render(request, "index.html",{})
else:
sign_in_detail = SignInForm()
message_detail = DropUsaMessage()
context={
"form":sign_in_detail,
"forms":message_detail
}
return render(request, "index.html",context)
index.html
<div class="container contact-form">
<form method="post">
<h3>Drop Us a Message</h3>
{% csrf_token %}
{{ forms }}<br><br>
<div class="form-group">
<input type="submit" name="SendMessage" class="btnContact" value="Send Message" />
</div>
</form>
</div>
<div class="container-fluid">
<div class="row">
<div class="col-md-8">
<img src="{% static 'img/sampleImage.jpg' %}" width="100%" height="100%" class="d-inline-block align-top" alt="">
</div>
<div class="col-md-4">
<form method="POST">
{% csrf_token %}
{{ form }}
<div class="form-check">
<span class="fpswd">Forgot password?</span>
</div>
<button type="submit" class="btn btn-primary" name="SignIn">Submit</button>
</form>
</div>
</div>
</div>
app/forms.py
from django import forms
from django.core import validators
class SignInForm(forms.Form):
email=forms.EmailField(widget=forms.EmailInput(attrs={"class": 'form-control',"placeholder":'Enter E-mail',"id": 'exampleInputEmail1'}))
password=forms.CharField(widget=forms.PasswordInput(attrs={"class":'form-control',"placeholder":'Enter Password',"id":'exampleInputPassword1'}))
class DropUsaMessage(forms.Form):
name = forms.CharField(widget=forms.TextInput(attrs={"class":'form-control',"placeholder":'Your Name'}))
email = forms.EmailField(widget=forms.EmailInput(attrs={"class": 'form-control',"placeholder":'Your E-mail',"id": 'exampleInputEmail1'}))
phone = forms.IntegerField(widget=forms.NumberInput(attrs={"class":'form-control',"placeholder":'Your Phone Number'}))
message = forms.CharField(widget=forms.Textarea(attrs={"class":'form-control',"placeholder":'Type Your Message',"style":'width:100%; height: 150px'}))
Expected Result:
After filling up the contact us form the field will be there.
Actual result:
there is no field in Contact us(except Send Message button) and no field in SignInForm(no e-mail and no password).
Just follow the code flow and you'll notice that in the case of a POST request and "SignIn" in the post, you return the rendered "index2.html" template without any context (form and forms will be undefined in your template). Idem for the other case:
return render(request, "index2.html", {}) # empty context
Worse, if the form posted is not valid, you'll notice you only define one for the forms and not the other one, so when the code execution reaches the line context = {"form": ..., "forms": ...}, one of them will be undefined and your view will "crash", return a 500 error.
context = {'form': sign_in_detail, # sign_in_detail never defined for second "if"
'forms': message_detail} # message_detail never define for first "if"
In general, when a POST is successful, you should always redirect to another view (or the same view). That's the internet convention, in order to avoid page reload issues that would resubmit the same data. Do this:
return redirect('home') # or whatever your homepage url pattern is called
Also, it would make more sense to post your forms to different views (change the action attribute of each of your <form> tags) so you can process them in separate views which is cleaner code:
<form action="{% url 'create_message' %}" method="post">

get_query_set(self) is not being called in django

I am implementing search function using TemplateView in Django the class is
class part_search_view(TemplateView):
model = part_list
context_object_name = 'part_list'
template_name = 'part_list.html'
def get_context_data(self, **kwargs):
context = super(part_search_view, self).get_context_data(**kwargs)
context['my_list'] = populate_nav_bar()
return context
def get_queryset(self):
key = self.request.GET['search_text']
partlist = part_list.objects.filter(Q(part_id__icontains=key) | Q(part_name__icontains=key))
return partlist
part_list.html
{% for part in part_list %}
{{ part.part_id }} - {{ part.part_name }}
<a href="{% url 'parts:part_update_view' part.id %}" > Edit </a>
{% endfor %}
the url mapping is
url(r'^search/',views.part_search_view.as_view(),name='part_search_view'),
the form for serch button
<form action="{% url 'parts:part_search_view'%}" role="form" class="navbar-form navbar-left" method="get" >
{% csrf_token %}
<div class="form-group ">
<input class="form-control mr-sm-2" type="text" placeholder="Search" name="search_text">
<button class="form-control search_buton btn btn-success " type="submit" >Search</button>
</div>
</form>
after the search button is pressed the address is
http://127.0.0.1:8000/parts/search/?csrfmiddlewaretoken=PWjEw1hRsyH9B6YcseVuhS0urX8L7f170q9ucLF9hTPQPThulpgMSP4y5xhScCVr&search_text=mp6
but the get_query_set(self) is not called here the get_context_data(...) is called though, why?
TemplateViews don't know anything about querysets, so they never call a get_queryset method. You should subclass a more appropriate view, perhaps ListView.
If you look at docs, TemplateView does not have a method get_queryset(). Then, you would have to manually call it in the view.