Django - how to go back to previous view with parameters - django

I am relatively new with Django, this must be a common problem.
I have created a view to show a form to input date (using widget that returns separate fields):
when date is inserted, I call a function userPage(request, my_date)
that filters, processes and renders a page (user.html) showing a list of items.
def datePage(request):
user=request.user
context = {}
context['form'] = UserDateForm()
if request.GET:
date_yr = request.GET['food_date_year']
date_mo = request.GET['food_date_month']
date_day = request.GET['food_date_day']
my_date_string = date_yr+'-'+date_mo+'-'+date_day
my_date = datetime.strptime(my_date_string, "%Y-%m-%d").date()
return userPage(request,my_date)
return render(request, "date.html", context)
def userPage(request, my_date):
user=request.user
# process context using user, my_date
context={...:..., 'my_date': my_date}
return render(request,'user.html',context)
In user.html I include a URL to add an item:
</div>
<form action="{% url 'My_ItemCreate' %}" method="POST">
{%csrf_token%}
<button type="submit" class="btn btn-success">
<span class="glyphicon glyphicon-plus"></span>
</button>
</form>
</div>
'My_ItemCreate' points to a django.views.generic CreateView that creates an item.:
path('MyItemCreate/',views.My_ItemCreate.as_view(),name='My_ItemCreate'),
class My_ItemCreate(CreateView):
model = MyItem
fields = ...
After creating the item in the CreateView, how do I go back to the user page
after I inserted the date? I have lost the date in the new URL.
If I use URL resolver to go to userPage, how do I pass a date in the format?
It would be nice that I am able to pass initial values in the CreateView, and
make some fields read-only, how do I modify/override CreateView ?
Many Thanks for your help!

I have found an answer to my problem: using request.session
to store a value and retrieving in other views, it works fine.
I am still curious to know if there are experts who
would provide a more elegant solution, and if someone
could be so kind to answer point 2) regarding CreateView read_only fields
Thanks
D

Related

Django & AJAX to show DB objects upon user's input submission

I'm pretty new in the Web development world, have been using Django so far.
I've been trying to figure out how to render data back to page after clicking on a submit button, so I see I'll need to use AJAX for that purpose.
I've created a very simple app just to understand the basics of AJAX.
However, googling, I couldn't really find a basic straight-forward implementation so I kinda got lost...
What I'm trying to achieve:
I have a model called Country:
class Country(models.Model):
name = models.CharField(primary_key=True, max_length=35)
continent = models.CharField(max_length=10)
capital = models.CharField(max_length=35)
currency = models.CharField(max_length=10)
And a super simple main page that asks the user to insert some country name.
The idea is to bring to the page all the info from the DB.
So it would look like this:
Main page HTML body:
<body>
<h2><em>Please type a country name:</em></h2><br><br>
<div class="container">
<form id="get_info" method="post">
{{ form }}
{% csrf_token %}
<input id="submit_button" type="submit" name="submit" value="Get info">
</form>
</div>
</body>
views.py:
from django.shortcuts import render
from country_trivia import forms
def main_page(request):
get_info_form = forms.GetInfo()
return render(request, 'country_trivia/index.html', {'form': get_info_form})
forms.py:
from django import forms
class GetInfo(forms.Form):
country_name = forms.CharField(label="")
I've seen some examples using forms, but I'm not even sure if it's needed, as I've seen some other examples that count on 'onclick' even listeners, then "grab" the text in the search field and pass it via AJAX...
How should I build my AJAX object for that simple purpose, and how should I integrate it?
Do I need to use forms at all?
I don't post anything to DB, just query it and print out data...
Thanks!!

Django update boolean field with a form

My simple web-application has two models that are linked (one to many).
The first model (Newplate) has a boolean field called plate_complete. This is set to False (0) at the start.
questions:
In a html page, I am trying to build a form and button that when pressed sets the above field to True. At the moment when I click the button the page refreshes but there is no change to the database (plate_complete is still False). How do I do this?
Ideally, once the button is pressed I would also like to re-direct the user to another webpage (readplates.html). This webpage does not require the pk field (but the form does to change the specific record) Hence for now I am just refreshing the extendingplates.html file. How do I do this too ?
My code:
"""Model"""
class NewPlate(models.Model):
plate_id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
title = models.CharField(max_length=200)
created_date = models.DateTimeField(default=timezone.now)
plate_complete = models.BooleanField()
"""view"""
def publish_plates(request,plate_id):
newplate = get_object_or_404(NewPlate, pk=plate_id)
newplate.plate_complete = True
newplate.save()
#2nd method
NewPlate.objects.filter(pk=plate_id).update(plate_complete = True)
return HttpResponseRedirect(reverse('tablet:extendplates', args=[plate_id]))
"""URLS"""
path('readplates', views.read_plates, name='readplates'),
path('extendplates/<pk>/', views.show_plates, name='showplates'),
path('extendplates/<pk>/', views.publish_plates, name='publishplates'),
"""HTML"""
<form method="POST" action="{% url 'tablet:publishplates' newplate.plate_id %}">
{% csrf_token %}
<button type="submit" class="button" value='True'>Publish</button></form>
-------Added show plates view:---------
def show_plates(request,pk):
mod = NewPlate.objects.all()
newplate= get_object_or_404(mod, pk=pk)
add2plate= Add2Plate.objects.filter(Add2Plateid=pk)
return render(request, 'tablet/show_plates.html', {'newplate': newplate,'add2plate': add2plate})
Thank you
The problem is two of your urls have the same pattern 'extendplates/<pk>/'. Django uses the first pattern that matches a url. I suppose that one of these view views.show_plates is meant to display the form and the other views.publish_plates is meant to accept the posted form data.
This means that simply both of these views should simply be a single view (to differentiate if the form is submitted we will simply check the requests method):
from django.shortcuts import redirect, render
def show_plates(request, plate_id):
newplate = get_object_or_404(NewPlate, pk=plate_id)
if request.method == "POST":
newplate.plate_complete = True
newplate.save()
return redirect('tablet:extendplates', plate_id)
context = {'newplate': newplate}
return render(request, 'your_template_name.html', context)
Now your url patterns can simply be (Note: Also captured arguments are passed as keyword arguments to the view so they should be consistent for your view and pattern):
urlpatterns = [
...
path('readplates', views.read_plates, name='readplates'),
path('extendplates/<uuid:plate_id>/', views.show_plates, name='showplates'),
...
]
In your form simply forego the action attribute as it is on the same page:
<form method="POST">
{% csrf_token %}
<button type="submit" class="button" value='True'>Publish</button>
</form>
You should avoid changing state on a get request like your view does currently.
Handle the POST request and change the data if the request is valid (ensuring CSRF protection).
def publish_plates(request,plate_id):
newplate = get_object_or_404(NewPlate, pk=plate_id)
if request.method == "POST":
newplate.plate_complete = True
newplate.save(update_fields=['plate_complete']) # a more efficient save
#2nd method
NewPlate.objects.filter(pk=plate_id).update(plate_complete=True)
return HttpResponseRedirect(reverse('tablet:extendplates', args=[plate_id]))
You could also put a hidden input in the form, or make a form in Django to hold the hidden input, which stores the plate_id value and that way you can have a generic URL which will fetch that ID from the POST data.
Now the real problem you've got here, is that you've got 2 URLs which are the same, but with 2 different views.
I'd suggest you change that so that URLs are unique;
path('extendplates/<pk>/', views.show_plates, name='showplates'),
path('publish-plates/<pk>/', views.publish_plates, name='publishplates'),

Add value on IntegerField with a button(Django 1.11)

models.py
likes = models.IntegerField()
forms.py
class LikeForm(forms.ModelForm):
class Meta:
model = Post
fields = ("likes",)
How could I make a button that add 1 to this IntegerField every time it's clicked? The default value is 0.
I'm guessing I have to use "submit" button but I'm not sure how I can do that with out rendering the form on the page.
In your views.py you could add something like this:
def record_like_view(request, pk):
if request.method == 'POST':
post = Post.objects.get(pk=pk)
post.likes += 1
post.save()
...
Then in your template:
<form method="post">
{% csrf_token %}
<a class="btn" href="{% url 'my_app:record_like' post.id %}">Like</a>
</form>
You are just posting to a URL. Even though you are still using a form in your template, there is no need for the LikeForm in this case. Take a look at this Django tutorial for another example.
As far as a user preventing a user from clicking multiple times (as pointed out by guillermo) this would require something a little more complicated.

How can i add several copies of one form and submit them with different data? Flask, WTForms

Hellow. I have a document database and flask app that gives me web-based opportunity to see the db's docs, add them and delete. Every doc has only it's number and name.
Usually I add the documents one by one, cause i have the WTForm -
class addDocForm(FlaskForm):
doc_name = StringField('Название документа', validators=[DataRequired()])
doc_number = StringField('Исходящий номер', validators=[DataRequired()])
the .html code -
<form action="" method="post" >
{{ form.hidden_tag() }}
<div class="row">
<label>{{ form.doc_name.label }}</label>
{{ form.doc_name(size=32) }}
</div>
<div class="row">
<label>{{ form.doc_number.label }}</label>
{{ form.doc_number(size=32) }}
</div>
<div class="row">
<button type="submit">Добавить</button>
</div>
</form>
and some /route logic -
#app.route('/add_doc', methods=['GET', 'POST'])
#login_required
def add_doc():
form = addDocForm()
if form.validate_on_submit():
doc = Doc(doc_name=form.doc_name)
if Doc.query.filter_by(doc_name=form.doc_name.data).first() == None:
db.session.add(doc)
db.session.commit()
So I add each document one by one filling this form and submitting it again and again. Now i've been tired. I want to save my energy by reducing number of clicking on submit button. Of course it's a joke, but the question is really about thing like this:
how can i add several copies of this 'addDocForm' on one page, fill the fields of these copies and click submit only once?
Is there any clever way to do that? I want to add for example 5-7 docs at once without the necessity to add them one by one. Let's suppose i've load the page with my form (one form) fill the fields, and than clicked '+' button and there appear another form.. fill the fields-> '+' button .. again. After all click the 'submit' button and all the data from filled fields by turns go to the data base. Is it real? any ideas? p.s. i have an idea on how to make it using clear sql + html + js, without flask-wtforms, sqalchemy and so on.. but i guess this is wrong way cause half of my app is already written using them. ) so many text, don't sure if anyone reach this point.. but still - help me, pls (((((
You could construct a MegaForm using field enclosures.
For example (untested):
from wtforms import StringField, FormField, FieldList
class AddDocForm(FlaskForm):
doc_name = StringField('Название документа', validators=[DataRequired()])
doc_number = StringField('Исходящий номер', validators=[DataRequired()])
class MegaForm(FlaskForm):
documents = FieldList(FormField(AddDocForm), min_entries=7, max_entries=7)
#app.route('/add_doc', methods=['GET', 'POST'])
#login_required
def add_doc():
form = AddDocForm()
if form.validate_on_submit():
for idx, data in enumerate(form.documents.data):
doc = Doc(doc_name=data["doc_name"])
if Doc.query.filter_by(doc_name=data["doc_name"]).first() == None:
db.session.add(doc)
db.session.commit()

How edit data from form django admin

I'm learning Django Framework, and I have a question. To help you understand I will try and explain using the example below:
Suppose that we have some table in db as is:
CREATE TABLE names (id INT NOT NULL PRIMARY KEY AUTO_INCREMENT, name VARCHAR(100));
And I have the form in Django Admin as is:
<form>
<textarea name="names"></textarea>
<input type="submit" name="sbt" value="Submit">
</form>
User entered something in the input names in the form and submitted it. Then a script catches this data and splits it into an array (str.split("\n")) and in cycle adding to table names!
And I many quetion:
How i can add form to Django Admin?
How i can catch form data and add this data to somethink table in database?
Thanks.
First of all you must create a django model.
Put this code in models.py.
class Names(models.Model):
name = models.CharField(max_length = 100)
Then you must create the admin model.
Put this code in admin.py.
class NamesAdmin(admin.ModelAdmin):
list_display = ['name']
# whatever you want in your admin panel like filter, search and ...
admin.site.register(Names, NamesAdmin)
I think it meet your request. And for split the names you can override save model method and split the names in there. But if you want to have an extra form, you can easily create a django model form.
Put the code somewhere like admin.py, views.py or forms.py
class NamesForm(forms.ModelForm)
class Meta:
model = Names
That's your model and form. So, if your want to add the form to django admin panel you must create a view for it in django admin. For do this create a view as common.
Put the code in your admin.py or views.py.
def spliter(req):
if req.method == 'POST':
form = NamesForm(req.POST)
if form.is_valid():
for name in form.cleaned_data['names'].split(' '):
Names(name = name).save()
return HttpResponseRedirect('') # wherever you want to redirect
return render(req, 'names.html', {'form': form})
return render(req, 'names.html', {'form': NamesForm()})
Be aware you must create the names.html and put the below code in you html page.
{% extends 'admin/base_site.html' %}
{% block content %}
<!-- /admin/names/spliter/ is your url in admin panel (you can change it whatever you want) -->
<form action="/admin/names/spliter/" method="post" >{% csrf_token %}
{{ form }}
<input type="submit" value="'Send'" >
</form>
{% endblock %}
This is your view and your can use it everywhere. But if you want only the admin have permission to see this page you must add this method too your NamesAdmin class.
def get_urls(self):
return patterns(
'',
(r'^spliter/$', self.admin_site.admin_view(spliter)) # spliter is your view
) + super(NamesAdmin, self).get_urls()
That's It. I hope this can help you.