Consider a basic Django form:
from django import forms
class TestForm(forms.Form):
first_name = forms.CharField(max_length=50)
last_name = forms.CharField(max_length=50)
When you pass this form request.POST, as in TestForm(request.POST), it receives the QueryDict instance from the requests's form(s):
from django.http.request import QueryDict
qd = QueryDict(mutable=True)
qd["first_name"] = "Brad"
qd["last_name"] = "Solomon"
TestForm(qd).is_valid()
# True
But now what I'd like to do is handle multiple row-like repetitions of these same two fields:
<form method="POST" action="/some/endpoint">
<input type="text" name="first_name">
<input type="text" name="last_name">
<br>
<input type="text" name="first_name">
<input type="text" name="last_name">
<input type="submit" value="OK">
</form>
I.e.
What's the proper way to iterate over each of these cleaned and validated (first_name, last_name) pairs?
If I pass the whole thing to a TestForm, then .cleaned_data only takes the last-seen pair:
>>> qd = QueryDict(mutable=True)
... qd.setlist("first_name", ["Brad", "Joe"])
... qd.setlist("last_name", ["Solomon", "Smith"])
>>> form = TestForm(qd)
>>> form.is_valid()
True
>>> form.cleaned_data
{'first_name': 'Joe', 'last_name': 'Smith'}
(For what it's worth, it does make sense that only the last value is shown because this mimics QueryDict behavior. However, I'd like to access all values rather than just the last-seen.)
If you can't use a formset, you'll just have to do things manually.
The rough idea here is
def view(request):
# (add the usual `if method == POST` stuff here)
first_names = request.POST.getlist('first_name')
last_names = request.POST.getlist('last_name')
thingamabobs = request.POST.getlist('thingamabob')
for first_name, last_name, thingamabob in zip(first_names, last_names, thingamabobs):
f = NameForm(data={
'first_name': first_name,
'last_name': last_name,
'thingamabob': thingamabob,
})
if not f.is_valid:
# ... do something here
f.save() # or something
Getting the data back to the view to re-populate the fields post-POST should things not validate will also be a little annoying here.
Related
I have a model which named Record, the model have the source code info.
Now, I want add a button in the admin site.
Current django admin.py code like:
#admin.register(Record)
class ControlRecord(admin.ModelAdmin):
list_display = ["file_path", "go_to_src_code", "func_info", "line"]
search_fields = ['file_path', 'func_info']
list_filter = ['severity', 'is_misinformation']
actions = [jump_to_src_code]
def go_to_src_code(self, obj):
return format_html(
'<button onclick= "" >Go</button>'
)
go_to_src_code.short_description = "GoToSrcCode"
I want to specify the specified method after clicking the button, what should i do?
it depends on the next action.
You want to call js function on the list view:
def jump_to_src_code(*args, **kwargs):
print('it works in python')
#admin.register(Record)
class ControlRecord(admin.ModelAdmin):
list_display = ["file_path", "go_to_src_code", "func_info", "line"]
search_fields = ['file_path', 'func_info']
# list_filter = ['severity', 'is_misinformation']
actions = [jump_to_src_code]
class Media:
js = ('myjs.js')
def go_to_src_code(self, obj):
return mark_safe('<button onclick= "alert(\'it works in js\'); // call function from myjs.js" >Go</button>')
go_to_src_code.short_description = "GoToSrcCode"
by click you start the js:
You want call the special admin action:
def jump_to_src_code(*args, **kwargs):
print('it works in python')
#admin.register(Record)
class ControlRecord(admin.ModelAdmin):
list_display = ["file_path", "go_to_src_code", "func_info", "line"]
search_fields = ['file_path', 'func_info']
# list_filter = ['severity', 'is_misinformation']
actions = [jump_to_src_code]
# class Media:
# js = ('myjs.js')
def go_to_src_code(self, obj):
action_name = 'jump_to_src_code'
action_index_in_action_list = '1'
return mark_safe(f"""
<input type="hidden" name="_selected_action" value="{obj.id}">
<input type="hidden" name="action" value="{action_name}">
<button type="submit" name="index" value="{action_index_in_action_list}">Go</button>"""
)
go_to_src_code.short_description = "GoToSrcCode"
this gives you call your action function in python.
thanks for comments:
ia am agree, that not all works in example before, for admin action call.
i've made some improvements:
def go_to_src_code(self, obj):
action_name = 'jump_to_src_code'
action_index_in_action_list = '1'
return mark_safe(f"""
<label class="your_style">Go
<input type="checkbox" name="_selected_action" onchange="this.nextElementSibling.disabled=false;this.nextElementSibling.nextElementSibling.disabled=false;this.form.submit()" value="{obj.id}" style="display:none;">
<input type="hidden" disabled name="action" value="{action_name}">
<input type="hidden" disabled name="index" value="{action_index_in_action_list}">
</label>"""
)
Why we need that:
inputs for action ("index", "action") - they should be disabled before. Otherwise other action not works.
input for obj.pk ("_selected_action") - this should be checkbox. Otherwise browser send list of objects.pk.
I don't like this improvement, in my opinion here should be new view with redirect and button with "formaction" attribute. But it works.
Can't understand, why this code doesn't work, i was trying to understand, but i'm only learning Flask&Py, help me please:
Flask:
#freepylib.route('/register', methods=['POST', 'GET'])
def signup_user():
if request.method == "POST":
username, useremail, userpasswd=request.form['name'], request.form['email'], request.form['password']
reg = Users(uname=username, useremail=useremail, upasswrd=userpasswd)
try:
mydb.session.add(reg)
mydb.session.commit()
token = reg.get_token()
return {'access_token': token}
except Exception as e:
return str(e)
else:
return render_template("register.html")
HTML code:
<form method="POST" name="reg" id="reg">
<input type ="text" class ="fadeIn second" name ="name" placeholder="name">
<input type="text" class="fadeIn second" name="email" placeholder="email">
<input type="text" class="fadeIn third" name="password" placeholder="password">
<input type="submit" name="subm" class="fadeIn fourth" value="Sign Up">
</form>
Error:
(pymysql.err.IntegrityError) (1048, "Column 'uname' cannot be null") [SQL: INSERT INTO `PFL_USERS` (uname, email, upasswrd) VALUES (%(uname)s, %(email)s, %(upasswrd)s)] [parameters: {'uname': None, 'email': None, 'upasswrd': None}] (Background on this error at: http://sqlalche.me/e/13/gkpj)
I think the problem is in your Users class, in the __init__. The kwargs need to match up with how you're calling the constructor from your app code.
For example, changing it to this should help:
def __init__(self, **kwargs):
self.uname = kwargs.get('uname')
self.email = kwargs.get('useremail')
self.upasswrd = kwargs.get('upasswrd')
Special thanks to #ChrisSears, you make my day
Problem was in class, which i forget to add(i'm really sorry)
So, i'll describe:
reg = Users(uname=username, email=useremail, upasswrd=hash_password(userpasswd))
Using this, i've transfer data into Class Users:
def __init__(self, **kwargs):
self.uname = kwargs.get('name')
self.email = kwargs.get('email')
self.upasswrd = kwargs.get('password')
As we can see, name of variables is not correct, so there is the problem, it should be like this:
def __init__(self, **kwargs):
self.uname = kwargs.get('uname')
self.email = kwargs.get('email')
self.upasswrd = kwargs.get('upasswrd')
I am generating 6 digits random OTP in my views.py and I just want to insert those many OTPs to be inserted into my Table at once using single QuerySet. Here is my code:
home.html
<form class="" action="" method="post">
{% csrf_token %}
<div class="" style="text-align: center:">
Mobile : <input type="text" name="mobile" value="">
<input type="submit" name="submit" value="Generate OTP">
</div>
</form>
models.py
class otpModel(models.Model):
mobile = models.CharField(max_length=30, blank=False, null=True)
otp = models.CharField(max_length=50, blank=False, null=True)
def __str__(self):
return self.mobile
views.py
import random
from app_name.models import otpModel
def registerOTP(request):
if request.method == 'POST':
mobile = request.POST['mobile']
for i in range(100):
otp = random.randint(100000, 999999)
otp = otpModel.objects.create(mobile=mobile, otp=otp)
if otp:
messages.info(request, 'OTP is saved!!!')
return render(request, 'app_name/otp.html')
else:
return render(request, 'studentexams/otp.html')
Here whenever I enter the Mobile number in the HTML form then only 1 OTP is inserted in the Table. Is there any solutions that I can insert multiple OTPs at a time using single QuerySet
Please give the Code snippet for achieving this task to be done
Try the following
from itertools import islice
def registerOTP(request):
if request.method == 'POST':
mobile = request.POST['mobile']
otps = []
for i in range(100):
otp = random.randint(100000, 999999)
otp_obj = otpModel(mobile=mobile, otp=otp)
otps.append(opt_obj)
# I advise you to do the following in a celery task
# Doing it in the request thread is a bad User experience
batch_size = 50 # 50 is the batch size, you
# can change it to any number you want based on your usecase
while True:
batch = list(islice(otps, batch_size))
if not batch:
break
otpModel.objects.bulk_create(batch, batch_size)
del otps[0:batch_size]
if otp:
messages.info(request, 'OTP is saved!!!')
return render(request, 'app_name/otp.html')
else:
return render(request, 'studentexams/otp.html')
There is a manager method called bulk_create. Using this method you can create list of objects into the database.
bulk_create(objs, batch_size=None, ignore_conflicts=False)
Your code snippet will be like:
otpModel.objects.bulk_create([otpModel(mobile=mobile, otp=otp1),
otpModel(mobile=mobile, otp=otp2),
])
This method inserts the provided list of objects into the database in an efficient manner (generally only 1 query, no matter how many objects there are):
Example:
Entry.objects.bulk_create([
... Entry(headline='This is a test'),
... Entry(headline='This is only a test'),
... ])
You can check out the official documentation here.
I have a form that's being given a dictionary of selection, it populates it correctly but on form submit it is not valid. When attempting to print errors, non_field_errors there are just blanks. When I am redirected to the form, now the choice field is populated by one choice and the csrf token from previous submit.
I've tried assigning choices in different ways such as self.fields['calendar'] = forms.ChoiceField(choices=choice_list) directly assign in a different way. self.fields['calendar'].choices = choice_list, a custom validator that ignores the validation, and inline debugging.
Form model:
class CalendarSelectionForm(forms.Form):
calendar = forms.ChoiceField(label="Calendar")
def __init__(self, calendars=None, *args, **kwargs):
super(CalendarSelectionForm, self).__init__(*args, **kwargs)
choice_list = [(calendar_id, calendar_name) for calendar_id, calendar_name in calendars.items()]
if calendars:
self.fields['calendar'].choices = choice_list
View:
if request.method == "POST":
print(request.POST)
cal_sync_form = CalendarSelectionForm(request.POST)
print("Non-field errors " + str(cal_sync_form.non_field_errors()))
print("Reg form errors " + str(cal_sync_form.errors))
# print("Field val " + str(cal_sync_form.calendar))
print("Field data " + str(cal_sync_form.data))
print("Field fields " + str(cal_sync_form.fields) + " Form is " + str(cal_sync_form.is_valid()))
if cal_sync_form.is_valid():
data = cal_sync_form.cleaned_data
print(data)
return render(request, 'management/gcal_sync_dashboard.html')
else:
return render(request, 'management/acct_select.html', {'form': cal_sync_form})
Form template:
<form class="form-import" action="/manage/gcal/sync/" method="post" id = "">
{% csrf_token %}
{{ form.calendar }}
{{ form.errors }}
{{ form.non_field_errors }}
<div class="push clearfix"></div>
<div class="col-sm-6 no-pad push"><input class="btn btn-brand btn-little button filson push-half" type="submit" value="Select email"><i class="fa fa-plus"></i></input>
</div>
</form>
The goal is to validate a posted form, the current print statements print out
<QueryDict: {'csrfmiddlewaretoken': ['sJHE8JJAzmeS0nRjaYZg5KdMlevJiInYY0G4YFJeITH1cVjciIdR1Dq1N28loUIL'], 'calendar': ['email#email.io']}>
Non-field errors
Reg form errors
Field data {}
Field fields OrderedDict([('calendar', <django.forms.fields.ChoiceField object at 0x117323080>)]) Form is False
In your view, you make a call to the CalendarSelectionForm constructor with request.POST as first positional argument. So that means that you call the __init__ function, and request.POST is passed as the calendars parameter.
You can fix this by constructing your form with named parameters. You furthermore will need to pass the same parameter to calendars as you did when you rendered the form with the GET request, since otherwise the choices do not per se match, and the user might have picked an option that is in that case not available during the POST request. Like:
if request.method == 'POST':
cal_sync_form = CalendarSelectionForm(calendars=my_calendars, data=request.POST)
# ...
with my_calendars the same value you pass when you constructed the form in the GET case.
My first foray into Django, allows the user to input a day of the week, and searches my database for restaurants open on that given day. Right now, the restaurant objects (Resto) have a days_open attribute, with each day separated by a comma (Monday, Tuesday, etc...).
When I input a day, the resulting page only displays the title and '[]' and I can't seem to get it to return the list of Resto objects. What is displaying the square brackets, and how do I go about fixing it to display the results of the search?
The code is below- my apologies if I neglected to include any relevant bits.
my forms.py:
from django import forms
from .models import Resto
class RestoSearch(forms.ModelForm):
class Meta:
model = Resto
fields = ('title', 'description', 'opening_hour', 'closing_hour')
models.py:
from django.db import models
class Resto(models.Model):
title = models.CharField(max_length=300)
description = models.TextField()
opening_hour = models.TimeField(auto_now=False, auto_now_add=False, null=True)
closing_hour = models.TimeField(auto_now=False, auto_now_add=False, null=True)
days_open = models.TextField(blank=True)
views.py:
from django.shortcuts import render
from django.http import Http404
from django.shortcuts import HttpResponse
from belize.models import Resto
from django.core.exceptions import *
from .forms import RestoSearch
def index(request):
return render(request, 'form.html')
def search(request):
form = RestoSearch()
if request.method == 'POST':
search_id=request.POST.get('textfield', None)
try:
#I think this is where my problem is
available = Resto.objects.filter(days_open = search_id)
html = ("<H1>Hello</H1>", available)
return HttpResponse(html)
except Resto.DoesNotExist:
return HttpResponse("Please try another day")
else:
return render(request, 'belize/form.html')
def restaurant_detail(request, id):
try:
restaurant = Resto.objects.get(id=id)
except Resto.DoesNotExist:
raise Http404('This restaurant does not exist')
return render(request, 'belize/restaurant_detail.html', {
'restaurant': restaurant,
})
template form.html:
<form method="POST" action="/search/">
{% csrf_token %}
<input type="text" name="textfield">
<button type="submit">Enter a day of the week</button>
</form>
I presume what you are trying to show is the RestoForm in that case the index method is not correct. It should be
def index(request):
form = RestoForm()
return render(request, 'form.html', {'form': form })
And then your template should change as
<form method="POST" action="/search/">
{% csrf_token %}
{{ form }}
<button type="submit">Enter a day of the week</button>
</form>
For additional details please see the examples at https://docs.djangoproject.com/en/1.9/topics/forms/#the-template
The [] means that your .filter() returned no results, its not surprising as you have a few issues with your code, lets start from the top:
You are declaring a form that you never use.
You are trying to catch an exception that is never raised by .filter()
You filter condition will only work for exact matches.
I've annotated your code as well:
def search(request):
form = RestoSearch() # You aren't using this form anywhere in your code?
if request.method == 'POST':
# Where is 'textfield' coming from?
search_id = request.POST.get('textfield', None)
try:
# If search id is "Tuesday", and a restaurant is
# open on monday and tuesday, so it has "Monday,Tuesday"
# in the days_open field, then this search will not
# return any results, because its looking for an exact
# match
available = Resto.objects.filter(days_open=search_id)
html = ('<H1>Hello World</H1>', available)
return HttpResponse(html)
except Resto.DoesNotExist:
# This exception is not raised by .filter(),
# .filter() will return an empty list, [] if no results are found
# so this entire try/except is not doing anything
return HttpResponse("Please try another day")
else: # in your code, this else is not indented correctly
return render(request, 'belize/form.html')
So there is a lot going on here, lets try something simple, starting with the template:
{% if results %}
{% for restaurant in results %}
{{ restaurant }}
{% endfor %}
{% else %}
Sorry, no results for your search. Try again.
{% endif %}
<form>
{{ form }}
<input type="submit" name="Search" />
</form>
Next, the search form:
class SearchForm(forms.Form):
search_field = forms.CharField('Search', strip=True)
Finally the view:
from django.db.models import Q
def search(request):
form = SearchForm(request.GET)
results = [] # Set results to an empty list
if form.is_valid():
needle = form.cleaned_data['search_field'].capitalize()
results = Resto.objects.filter(Q(days_open__startswith='{},'.format(needle)) |
Q(days_open__endswith=',{}'.format(needle)) |
Q(days_open__contains=',{},'.format(needle)) |
Q(days_open='{},'.format(needle)) |
Q(days_open='{}'.format(needle)))
return render(request, 'search.html', {'results': results, 'form': form})
Lets assume the user entered 'Thursday' as a search field. In this view you are searching for all restaurants whose days_open field:
Either starts with Thursday, or
Ends with ,Thursday or
Contains ,Thursday, in the middle or
Has the value Thursday, or
Has the value Thursday
Your template will then only show the results if there are any values to display; since an empty list [] is false, then the {% if results %} condition will fail, so on empty lists the template will display the error notice instead.
In your view, you only do the database check if someone enters something in the search field, that's what if form.is_valid(): does. In django, by default all form fields are required - so a blank form will return an error. Using this trick, we make sure we only search if someone enters a value in the search box.
The main action happens with all the Q() calls. A Q object is a way to do multiple queries and chain them together. It is used whenever you want to do an "or" or "and" type query. Our search is an "or" type query, because we want to return any results if the value in days_open matches any number of conditions.