Calling Django function from html - python

I am having trouble running a function in the view from an html button but the code seems to be right so if anyone knows what I am missing I would greatly appreciate it. The ultimate goal is to start a celery task so if there is a better way to do that please let me know.
template.html:
<form action="{% url 'save' pk=project.id %}" method="POST">
{% csrf_token %}
<button id="saveChanges" type="submit" class="btn btn-primary btn-sm">Save</button>
</form>
urls.py:
urlpatterns = patterns('',
....
url(r'^manage/save(?P<pk>\d+)/$', 'myapp.views.Save', name="save"),
....)
views.py
def Save(request, pk):
if request.method == 'POST':
project = Project.objects.get(id = pk)
....
task.delay(project.id)
return HttpResponse('OK', status=200)

#DoctorWizard run your server with manage.py runserver -- the log will be on the same terminal.

Your url pattern is using named groups which will pass keyword arguments to the view. Your view is only accepting positional arguments.
Either change the URL pattern to r'^manage/save(\d+)/$' (did you want a slash after save?)
OR
Change the view arguments to def Save(request, pk=None): or def Save(request, **kwargs): and pull pk from kwargs.
https://docs.djangoproject.com/en/1.6/topics/http/urls/#named-groups

Related

Django 1.10 : view function() takes exactly 2 arguments (1 given)

This is my first django project and I'm struggling to finish it.
I've been working to function that editing post. When user clicks button, it send no(int)for that article, and get information related to no and display on page. User can edit that post in the same form and when user click submit, it redirect to home.html
However, the function I made keep sending me an error message that it takes 2 arguments even though I did not use any function that takes 2 arguments.
Here is views.py
#login_required
def edit_article(request, article_no):
article = Article.objects.filter(pk=article_no)
form = ArticleForm(request.POST, instance=request.article)
if form.is_valid():
form.save()
messages.add_message(request, messages.SUCCESS, _('Article correctly saved.'))
# If the save was successful, redirect to another page
redirect_url = reverse('blog/home.html')
return HttpResponseRedirect(redirect_url)
else:
form = ArticleForm(instance=request.article)
return (request, {'form': form}, context)
This is form in detail.html where send no value to edit_article.html
<form action="{% url 'blog:edit_article' %}" method="post" style="display: inline;">
{% csrf_token %}
<input type="hidden" name="no" value="{{ item.no }}" />
<button type="submit">edit></button>
</form>
The article_no arg does not magically find its way into the function call via the POST submit. You need to provide it to the url tag:
{% url 'blog:edit_article' item.no %}
This assumes, of course, that you have a url pattern with an appropriate named group associated with this view/view name.
If You are talking about this function, it does recieve more than one Arg, it recieves the No you are talking about, and the request object
def edit_article(request, article_no):
...
If your view needs arguments you must give the arguments in the url templatetag, like this :
{% url 'accounts:detail_account' username = username %}

Django render different templates for two submit buttons in a form

I am a beginner at Django development, and I am trying to make a food diary application. After a user enters his email on index.html, another web page should be rendered according to whichever button he clicks.
I can possibly add two templates, but I also want my app to work if a user manually types a valid URL such as /apps/<user_email>/addDiaryEntry/. I don't know what to add in /apps/urls.py. Also, can I somehow access a user object's Id so my routing URL become /apps/<user_id>/addDiaryEntry/ instead?
/templates/apps/index.html
<form method="post" action="/apps/">
{% csrf_token %}
<label for="email_add">Email address</label>
<input id="email_add" type="text">
<button type="submit" name="add_entry">Add entry</button>
<button type="submit" name="see_history">See history</button>
/apps/views.py
def index(request):
if request.POST:
if 'add_entry' in request.POST:
addDiaryEntry(request)
elif 'see_history' in request.POST:
seeHistory(request)
return render(request, 'apps/index.html');
def addDiaryEntry(request):
print ("Add entry")
def seeHistory(request):
print ("See history")
/apps/urls.py
urlpatterns = [
url(r'^$', views.index, name='index'),
]
Thank you for your help! Please feel free to share any best practices which I am not following.
1) passing in an argument into a url, you can use regex groups to pass arguments. Here is an example using a kwarg:
url(r'^(?P<user_email>[^#]+#[^#]+\.[^#]+)/addDiaryEntry/$', views.add_diary, name='add-diary-entry'),
2) just render a different template depending on which button was pressed:
def index(request):
if request.POST:
if 'add_entry' in request.POST:
addDiaryEntry(request)
return render(request, 'apps/add_entry.html');
elif 'see_history' in request.POST:
seeHistory(request)
return render(request, 'apps/see_history.html');
It's always tough starting out, make sure you put in the time to go over the docs, here are some places to look over regarding these topics:
https://docs.djangoproject.com/en/1.10/topics/http/urls/#named-groups
https://docs.djangoproject.com/en/1.10/topics/http/views/

Django NoReverseMatch at /qw-1/

Im new to django and was struck by using slug, now Im confused how to use the ID parameter and convert to slug
URL.py
url(r'^deletePost/(?P<slug>[\w-]+)/$', views.delete_post, name='delete_post')
Template
<form method="POST" action="{% url 'delete_post' id=post.id %}">{% csrf_token %}
<button type="submit" class="btn btn-danger"> &nbsp Delete</button>
</form>
Views.py
def delete_post(request,slug):
posts=Post.objects.get(slug=slug)
if request.method == 'POST':
posts.delete()
return redirect("home")
How can i use slug & id to delete the post which is created
Any help is appreciated. Thanks in advance
Error for reference
In my opionion, you dont want to convert the id to slug. You can just make your application flexible enough so that you could delete by either slug or id. You just need to handle the parameters accordingly.
So, you can do something like this:
urls.py
url(r'^deletePost/(?P<slug>[\w-]+)/$', views.delete_post, name='delete_post_by_slug'),
url(r'^deletePost/(?P<id>[0-9]+)/$', views.delete_post, name='delete_post_by_id')
And in the views:
def delete_post(request, slug=None, id=None):
if slug:
posts=Post.objects.get(slug=slug)
if id:
posts=Post.objects.get(id=id)
#Now, your urls.py would ensure that this view code is executed only when slug or id is specified
#You might also want to check for permissions, etc.. before deleting it - example who created the Post, and who can delete it.
if request.method == 'POST':
posts.delete()
return redirect("home")
Note that you can compress the 2 URL patterns into a single one - but this approach keeps it readable, and understandable. I shall let you figure out the URL consolidation once you are comfortable with the django framework, etc..
If you want to use both slug and id, your URL pattern should look like this:
url(r'^deletePost/(?P<slug>[\w-]+)-(?P<id>[0-9]+)/$',
views.delete_post, name='delete_post')
And your view should look like this:
def delete_post(request, **kwargs):
# Here kwargs value is {'slug': 'qw', 'id': '1'}
posts = Post.objects.get(**kwargs)
if request.method == 'POST':
posts.delete()
return redirect('home')
# ... (I guess this view does not end here)
And your template also have to set both:
<form method="POST" action="{% url 'delete_post' slug=post.id id=post.id %}">{% csrf_token %}
<button type="submit" class="btn btn-danger"> &nbsp Delete</button>
</form>

Django form redirect after POST is unreliable and needed a success_url

I am totally stuck to understand this behaviour and found a workaround I don't really like. Can anyone help enlighten me please? The context is I have a bootstrap styled form to create new records (inheriting from the generic.CreateView)
url.py:
url(r'^$', home, name='home'),
url(r'^main/$', views.MainView.as_view(), name='MainView'),
url(r'^topic/(?P<pk>[0-9]+)/$', catalogue_views.TopicView.as_view(), name='TopicView'),
url(r'^resource/(?P<pk>[0-9]+)/$', catalogue_views.DetailView.as_view(), name='ResourceDetail'),
url(r'^contribute/$', catalogue_views.ContributeView.as_view(success_url="/main/"), name='Contribute'),
views.py:
class ContributeView(generic.CreateView):
template_name = "openeye/contribute.html"
form_class = ContributeForm
#method_decorator(login_required)
def dispatch(self, *args, **kwargs):
return super(ContributeView, self).dispatch(*args, **kwargs)
class MainView(generic.ListView):
template_name = "openeye/main.html"
context_object_name = 'topic_list'
# TODO Make this only active topic areas?
def get_queryset(self):
return TopicArea.objects.all().order_by('name')
#method_decorator(login_required)
def dispatch(self, *args, **kwargs):
return super(MainView, self).dispatch(*args, **kwargs)
forms.py:
class ContributeForm(forms.ModelForm):
class Meta:
model = CatalogueItem
fields = ['title', 'topic_area', 'description', 'link', 'what_learn', 'how_apply', 'level', 'relevant_to', 'discovered_by']
ROLE_CHOICES = [[x.id, x.job] for x in JobType.objects.all()]
title = forms.CharField(widget=forms.TextInput(attrs={'placeholder': 'To sell this resource to others'}), max_length=80, required=True)
description = forms.CharField(widget=forms.Textarea(attrs={'rows': 2, 'placeholder': 'Clear, e.g. format, duration, activities...'}))
link = forms.CharField(widget=forms.URLInput(attrs={'placeholder': 'If required, link to resource http://...'}), required=False)
what_learn = forms.CharField(widget=forms.Textarea(attrs={'rows': 3, 'placeholder':"This is important,."}), label='What will you learn?')
how_apply = forms.CharField(widget=forms.Textarea(attrs={'rows': 3, 'placeholder':"How could this be put into action afterwards?"}), label='How could you apply this?')
relevant_to = forms.MultipleChoiceField(widget=forms.CheckboxSelectMultiple, choices=ROLE_CHOICES)
and a template with a form:
<div class="container">
<div class="entry-form row">
<div class="col-md-10 col-md-offset-1 col-sm-10 col-sm-offset-1 col-xs-10 col-xs-offset-1">
<form action="{% url 'MainView' %}" method="post" class="form">
<input type="hidden" name="next" value="{{ next }}">
{% bootstrap_form form %}
<button class="btn btn-primary btn-lg" type="submit">Submit Suggestion</button>
{% csrf_token %}
</form>
</div>
</div>
The form works perfectly and the data is saved nicely into the database. The problem is afterwards, the browser goes to the correct URL /main/ but the SCREEN IS BLANK. The server shows HTTP 405 0, and if I refresh the page it works.
If I alter the template so the action="{% url 'Contribute' %}" to return to the same form, I get HTTP 500 and a Django message about 'No URL to redirect to'. So two different errors determined by the re-direct location. In both case if I just click in browser url field and hit return it works.
I am sure this worked initially and then broke but I solved it as follows. Hard code in the success_url using it's path
url(r'^contribute/$', catalogue_views.ContributeView.as_view(success_url="/main/"), name='Contribute'),
Removing any action link in the template:
<form action="" method="post" class="form">
Is this the correct approach? Why, despite going to the correct URLs, do the pages not load or give errors with my original approach? I'd love to understand this.
Are you sure your data is actually saved on the server? From what you posted, it seems very unlikely. Here is the normal process followed by Django:
GET on form view (ContributeView)
→ returns an empty form
POST on form view (ContributeView)
→ if invalid, go back to step 1. If valid return a 302 Redirect to success_url.
GET on success_url
So normally, in your template, the form action should be empty, so the form gets posted back to the view that generated it. And the ContributeView should have a success url that redirects to wherever you want to send the user after:
from django.core.urlresolvers import reverse_lazy
class ContributeView(generic.CreateView):
# other stuff
success_url = reverse_lazy('MainView')
The behavior you get, with the 405, is because the browser, attempts to send the form directly to MainView which, not being a form view, tells the browser it does not know how to handle a POST method.

Is there a way to make Django-registration-redux work with `#login_required`?

I use Django-registration-redux in my project so I don't have to bother with all the login details. However, now I created a view with the login_required decorator and encountered a problem. The problem is that when the user is not logged in, then the decorator works by redirecting
/events/create_event/
to
/accounts/login/?next=/events/create_event/
Which is nice, but the particular URL is not in the registration-redux URLs, so the whole thing falls apart. Is there an elegant way to make django-registration-redux work with the #login_required decorator?
Login page
<form method="post" action="{% url 'login_view' %}?next=requst.META.HTTP_REFERER">
{{form.as_p}}
<input type="submit" value="Login">
</form>
settings.py
TEMPLATE_CONTEXT_PROCESSORS = (
....
"django.core.context_processors.request",
)
views.py
def login_view(request):
......
try:
return redirect(request.GET['next'])
except:
return redirect('/')
I solved the problem by changing the file registration\auth_urls.py
to add
url(r'^login/(?P<next>\?next=(.*))', auth_views.login, {'template_name':'registration/login.html'}, name='auth_login_redirect'),
and now, everything is working like it should.

Categories

Resources