Python Django How to save choice using model class "CHOICE"? - python

In my models.py, I have class with LEVEL_CHOICES and Level.
First I builded my project with a textfield Level and it worked. Then I decided to change my Level in order to give users only certain choices. Therefore I edited my models.py and I have now:
class Eleve(models.Model):
FIRST = 'FIRST'
SECOND = 'SECOND'
THIRD = 'THIRD'
LEVEL_CHOICES = (
('FIRST', 'School'),
('SECOND', 'HighSchool'),
('THIRD', 'University'),
)
Level = models.CharField(max_length=3, choices=LEVEL_CHOICES, default='FIRST')
I think that there is a problem in my views.py because I'am able to save class Eleve through Admin app. I'm also using a decorator in order to have REcaptchaV2.
def Form(request):
form = ResgisterStud(request.POST)
if request.recaptcha_is_valid and form.is_valid():
form.save(commit=False)
form.save()
return render(request, 'form.html', {'form': form})
else:
return render(request, 'form.html', {'form': form})
my forms.py
class ResgisterStud(forms.ModelForm):
class Meta:
model = Eleve
My Form.html
<form action="{% url "form" %}" method="post" enctype="multipart/form-data">
{% csrf_token %}
<div class="col-md-6 form-group">
{{ form.Level|add_class:"form-control" }}
</div>
<script src='https://www.google.com/recaptcha/api.js'></script>
<div class="form-group g-recaptcha" data-sitekey="***"></div>
{% if messages %}
{% for message in messages %}
{{ message }}
{% endfor %}
{% endfor %}
{% endif %}
</form>

Related

Django Model form not rendering

Im trying to create a model form on django but it doesnt want to render even though I mapped it properly and created the path.
models.py
from django.db import models
# Create your models here.
Media_Choices = (
("TV", "TV"),
("Radio", "Radio"),
("Youtube", "Youtube"),
("Podcast", "Podcast"),
)
class Appear(models.Model):
Show = models.CharField(max_length=100)
Media = models.CharField(max_length=30, blank=True, null=True, choices=Media_Choices)
Episode = models.IntegerField()
Date = models.DateField(max_length=100)
Time = models.TimeField(auto_now=False, auto_now_add=False)
Producer = models.CharField(max_length=100)
Producer_Email = models.EmailField(max_length=254)
def __unicode__(self):
return self.Show + ' ' + self.Producer_Email
forms.py
from django import forms
from django.core.exceptions import ValidationError
from django.forms import ModelForm
from .models import Appear
class AppsForm(ModelForm):
class Meta:
model = Appear
fields = '__all__'
def clean_Producer_Email(self):
Producer_Email = self.cleaned_data.get('Producer_Email')
if (Producer_Email == ""):
raise forms.ValidationError('field cannot be left empty')
for instance in Appear.objects.all():
if instance.Producer_Email == Producer_Email:
raise forms.ValidationError('Please fill in correct email')
return Producer_Emailenter
views.py
from django.shortcuts import render
from .forms import AppsForm
# Create your views here.
def AppS(request):
form = AppsForm()
context = {'forms': form}
return render(request, 'AppsForm.html', context)
it refuse to render but it displays the html tag that is in the file but not the fields from the form. this is the html template
AppsForm.html
{% extends 'base.html' %}
{% block content %}
{% load crispy_forms_tags %}
<form action="" method="POST">
{% csrf_token %}
{{ form|crispy }}
<input type="submit" value="submit">
</form>
{% endblock %}
you view is wrong try this
def AppS(request):
if request.method == 'POST':
form = AppsForm(request.POST)
if form.is_valid():
form.save()
return redirect('/')
else:
form = AppsForm()
return render(request, 'AppsForm.html', {'form': form})
and in your html
<form method="POST" class="" action="">
{% csrf_token %}
{{ form|crispy }}
<input type="submit" class="" value="Submit">
</form>
now you are good to go and tell me if you still get error
Based on your AppS() function, where you are passing the form as 'forms' to your AppsForm.html you can render it with crispy_forms like:
AppsForm.html:
{% extends 'your_app/base.html' %}
{% block content %}
{% load crispy_forms_tags %}
<form method="POST" class="" action="">
{% csrf_token %}
{{ forms|crispy }}
<input type="submit" class="" value="Submit">
</form>
{% endblock %}
https://django-crispy-forms.readthedocs.io/en/latest/

Django puts my password in browser url field

I have the following view class:
class LoginView(View):
form_class = LoginForm
template_name = 'workoutcal/login.html'
def post(self, request):
form = self.form_class(request.POST)
if form.is_valid():
email = form.cleaned_data['email']
password = form.cleaned_data['password']
user = authenticate(email = email, password = password)
if user is not None:
if user.is_active:
login(request, user)
return calendar(request)
else:
return render(request, self.template_name, {'form':form})
else:
form['custom_error_message'] = 'Invalid user'
return render(request, self.template_name, {'form':form})
def get(self, request):
form = self.form_class(None)
return render(request, self.template_name, {'form':form})
And this template:
login.html
{% extends "workout/base.html" %}
{% block logoutwidget %}{% endblock %}
{% block content %}
<form action="/workoutcal/login/">
{% include "workoutcal/form_disp_errors.html" %}
<input type="submit" value="Log in">
</form>
{% endblock %}
form_disp_errors.html
{% csrf_token %}
{{ form.custom_error_message }}
{{ form.non_field_errors }}
{% for field in form.visible_fields %}
<div class="row">
<div class="col-xs-2">
{{ field.label_tag }}
</div>
<div class="col-xs-2">
{{ field }}
</div>
<div class="col-xs-3">
{{ field.errors }}
</div>
</div>
{% endfor %}
When I go to workoutcal/login, type in an incorrect username and password (user doesn't exist), the page goes to workoutcal/login again, but with this url:
http://localhost:8000/workoutcal/login/?csrfmiddlewaretoken=ZywQUh7gnNfaHi8FcA3be4ynLB7SpGgwdJ0UxGzUuRYp0G0Y9LQ9e24Jx8Q1OD3Y&email=myemail%40hotmail.com&password=MYPASSWORD
As you can see in the end of the link, the password is displayed. This is obviously not good. However, I can't understand why it happens. Any ideas?
You have to use HTTP method POST, for that you must set attribute method="post" to your form tag. Like that:
<form method="post" action="/workoutcal/login/" >
With method POST request will send query string (key/value pairs) in HTTP message body instead of URL.
Note: consider using PUT/PATCH to update objects and DELETE to remove for RESTful APIs (by default Django will use method POST for all these cases).

Django: return form data on the same page

How can I add "headers" to context so it's accessible in templates. I guess it needs to be pass to get request somehow? What would be the pythonic way to pass this variable to a get method in FormView class?
views.py
class IndexView(FormView):
template_name = 'index.html'
form_class = CheckForm
success_url = reverse_lazy('index')
def form_valid(self, form, **kwargs):
context = self.get_context_data(**kwargs)
context['headers'] = form.result()
return super(IndexView, self).form_valid(form)
index.html
<form action="." method="post" name="url" id="url" novalidate>
{{ form.as_p }}
{% csrf_token %}
<button type="submit">Check</button>
{% if headers %}
{% for k,v in headers %}
{{ k }}: {{ v }}<br>
{% endfor %}
{% else %}
<br>No data
{% endif %}
forms.py
from django import forms
import requests
class CheckForm(forms.Form):
url = forms.URLField(max_length=255, label='')
def result(self):
cd = self.cleaned_data
url = cd['url']
r = requests.get(url)
r.headers
return r
urls.py
urlpatterns = [
url(r'^$', views.IndexView.as_view(), name='index'),
]
I am pretty sure you want to use messages
from django.contrib import messages
So now you can add some stuff and send it to the next template.
messages.success(request, u"Success message - text only")
In template :
{% if messages %}
<ul class="messages">
{% for message in messages %}
<li{% if message.tags %} class="{{ message.tags }}"{% endif %}>
{% if message.level == DEFAULT_MESSAGE_LEVELS.ERROR %}Important: {% endif %}
{{ message }}
</li>
{% endfor %}
</ul>
{% endif %}
Main problem is the redirect on success. This will throw another get request to same window. In your case this is not desired as you want to capitalize on the data from the form.
views.py
import requests
class IndexView(FormView):
template_name = 'index.html'
form_class = CheckForm
def form_valid(self, form, **kwargs):
context = self.get_context_data()
context['headers'] = form.get_headers()
return self.render_to_response(context)
forms.py
import requests
from django import forms
class CheckForm(forms.Form):
url = forms.URLField(max_length=255, label='')
def get_headers(self):
if self.cleaned_data:
url = self.cleaned_data['url']
return requests.get(url).headers
return {}
index.html
<form action="." method="post" name="url" id="url" novalidate>
{{ form.as_p }}
{% csrf_token %}
<button type="submit">Check</button>
{% if headers %}
{% for k,v in headers.items %}
{{ k }}: {{ v }}<br>
{% endfor %}
{% else %}
<br>No data
{% endif %}
Had a similar requirement and passed the custom context (here a processed file) and the original form instance in form_valid() to a render method instead of the forms default HttpResponseRedirect.
# forms.py
class MyForm(forms.Form):
file = forms.FileField()
# views.py
class MyFormView(FormView):
template_name = 'path/to/template/mytemplate.html'
form_class = MyForm
def get_success_url(self):
return self.request.path
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['processed_data'] = None
return context
def do_some_processing(self, file):
# process file...
return processed_data
def form_valid(self, form):
form = super().form_valid(form)
custom_context = {
"form": self.form_class,
"processed_data": self.do_some_processing(self.request.FILES['file']),
}
context = self.get_context_data(form=form)
context.update(custom_context)
return render(self.request, self.template_name, context)
# mytemplate.html
<form method="POST" enctype="multipart/form-data">
{% csrf_token %}
{{ form.as_p }}
<button type="submit">Submit</button>
</form>
<pre>
{{ processed_data }}
</pre>

Upload CSV file in django admin list view, replacing add object button

I want to replace the add object button in the listview of an admin page. The underlying idea is that an administrator can download data on all models in the db, use a tool to edit the data, and then reupload as a CSV file.
In the list view I am struggling to override the form, as setting
class SomeModelForm(forms.Form):
csv_file = forms.FileField(required=False, label="please select a file")
class Meta:
model = MyModel
fields = '__all__'
class SomeModel(admin.ModelAdmin):
change_list_template = 'admin/my_app/somemodel/change_list.html'
form = SomeModelForm
other stuff
The admin change_list.html is overridden as follows:
{% extends "admin/change_list.html" %}
{% load i18n admin_urls admin_static admin_list %}
{% block object-tools-items %}
<form action="{% url 'admin:custom_submit_row' %}" method="post" enctype="multipart/form-data">
{% csrf_token %}
<p>
{{ form.as_p }}
</p>
<p><input type="submit" value="Upload" /><input type="reset" value="Reset"></p>
</form>
{% endblock %}
Previously SomeModel was missing the class Meta, as per sebbs response this is updated. The original error has been resolved but now currently the admin page is displaying the upload and reset buttons but no field for file uploads.
cheers
Edited with sebb's input below. Thanks sebb.
The error fixed was
< class ‘my_model.admin.SomeModelAdmin'>: (admin.E016) The value of 'form' must inherit from 'BaseModelForm'
OP here, solution is as follows:
class SomeModelForm(forms.Form):
csv_file = forms.FileField(required=False, label="please select a file")
class SomeModel(admin.ModelAdmin):
change_list_template = 'admin/my_app/somemodel/change_list.html'
def get_urls(self):
urls = super().get_urls()
my_urls = patterns("",
url(r"^upload_csv/$", self.upload_csv, name='upload_csv')
)
return my_urls + urls
urls = property(get_urls)
def changelist_view(self, *args, **kwargs):
view = super().changelist_view(*args, **kwargs)
view.context_data['submit_csv_form'] = SomeModelForm
return view
def upload_csv(self, request):
if request.method == 'POST':
form = MineDifficultyResourceForm(request.POST, request.FILES)
if form.is_valid():
# process form
with the template overridden as so:
{% extends "admin/change_list.html" %}
{% load i18n admin_urls admin_static admin_list %}
{% block object-tools %}
{% if has_add_permission %}
<div>
<ul class="object-tools">
{% block object-tools-items %}
<form id="upload-csv-form" action="{% url 'admin:upload_csv' %}" method="post" enctype="multipart/form-data">
{% csrf_token %}
<p>{{ form.non_field_errors }}</p>
<p>{{ submit_csv_form.as_p }}</p>
<p>{{ submit_csv_form.csv_file.errors }}</p>
<p><input type="submit" value="Upload" />
<input type="reset" value="Reset"></p>
</form>
{% endblock %}
</ul>
</div>
{% endif %}
{% endblock %}
The form needs some custom validation but otherwise this solves the difficult part of customizing the admin page.
To elaborate what is going on here:
get_urls is overridden so that an additional endpoint can be added to the admin page, this can point to any view, in this case it points upload_csv
changelist_view is overridden to append the form info to the view
the change_list.html template block "object-tools" is overridden with the form fields
Hopefully someone else finds this helpful as well.
to your class SomeModelForm add something like this:
class Meta:
model = YourModel
fields = '__all__'
and change from forms.Form to forms.ModelForm

Form not submitting information (Django)

I'm trying to create comments for a django blog project, but the form submission button does not doing anything when clicked.
Here is the html for the template.
<form role="form" method="post">
<div class="input-group">
{% csrf_token %}
{% for field in form %}
{{ field }}
{% endfor %}
<p>Comment: </p>
<span class="input-group-btn">
<button class="btn btn-default" type="submit">Submit</button>
</span>
</div>
</form>
Here is the view I'm trying to call when I press the button.
def detail(request, slug):
context ={}
post = BlogPost.objects.get(slug=slug)
# print(request.method)
if request.method=='POST':
form = CommentForm(request.POST)
else:
form = CommentForm()
if form.is_valid():
t = form.save(commit=False)
t.commentTime = datetime.datetime.now()
t.save()
return HttpResponseRedirect(reverse('blogpost_detail'))
comment_list=Comments.objects.order_by('-commentTime')[:25]
context = {'comment':comment_list,'form':form, 'post': post}
return render(request, 'blog/blogpost_detail.html', context)
And here is the form that is called in the template.
class CommentForm(forms.ModelForm):
class Meta:
model = Comments
fields=('commentText', 'commentImage',)
exclude =('post','commentTime',)
widgets={
'commentText': forms.Textarea(attrs={'col':10}),
}
Thanks for the help!
You need an action attribute in the form tag. Since you're posting to the same URL, it should be
<form action="." method="post " role°"form">

Categories