Say I have a url likes this
path(
'foo/<int:foo_id>/edit/',
views.FooView.as_view(),
name='foo',
),
and a view likes this:
def get(self, request, foo_id):
I find a common idiom is getting the URL variable foo_id into the context.
The only thing context has access to be default is request. I tried checking request and request.GET and could not see anything.
Is there a better way than:
Manually adding url variables to the context_data after get_context_data()
or passing it into get_context_data from a custom call from get? (ugly because class based views expect the same get_context_data signature)
The url parameters are stored in the .kwargs of the view. You thus can access and render these with:
{{ view.kwargs.foo_id }}
There is a reference with the name view that is passed which is the View object that is constructed when handling a request. We thus access the .kwargs attribute of that View object, and in the kwargs, we look for kwargs['foo_id'].
A peculiarity in Django is that a TemplateView [Django-doc] passes all it kwargs items as context data, if your view is thus a TemplateView, then you can render this with
<!-- only a TemplateView -->
{{ foo_id }}
Related
I'm trying to send an object obtained with a ModelChoiceField into a view via urls and views parameters by I don't think that's the right method. I don't know if I have to use POST or GET method.
When I use the GET method, the object is displayed in the url but the view stay the same.
With the POST method, django send errors messages about parameters.
EDIT : I don't remeber the exact messages, I can't recover them for now but as I remember they said something like Reverse for argument not found
My form
class IndividuForm(forms.Form):
individu = forms.ModelChoiceField(queryset=Individu.objects.all()
Here's the view with the form
def index(request):
individu_form = IndividuForm(request.GET or None)
if individu_form.is_valid():
individu_id = individu_form.cleaned_data['individu'].id
HttpResponseRedirect('%i/raison/' % individu_id)
return render(request, 'derog_bv/index.html', {'individu_form':individu_form})
The template where the form is displayed
<form action="{% url 'index' <!-- Errors when I put parameters here --> %} method="<!-- GET or POST -->">
{% csrf_token %}
{{ form }}
<input type="submit">Suivant</input>
</form>
The view where I want to get my object
def raison(request, """ error with this parameter : individu_id"""):
individu = get_object_or_404(Individu, pk=individu_id)
URLs
urlpatterns = [
path('', views.index, name='index'),
path('<int:individu_id>/raison/', views.raison, name='raison'),
]
Ok, so:
1/ you definitly want to use the GET method for your form (you're not submitting data for processing / server state change)
2/ I don't know why you'd want to pass parameters (nor which parameters FWIW) to the {% url %} tag in your index template - you're submitting the form to the index view, which doesn't expect any additional param. Actually you could just remove the action attribute of the HTML form tag since the default (submitting to the current url) is what you want.
3/ your views.raison prototype must match the url definition, so it has to be:
def raison(request, individu_id):
# code here
4/ in your index view:
HttpResponseRedirect('%i/raison/' % individu_id)
you want to build the url using django.core.urlresolvers.reverse instead :
from django.core.urlresolvers import reverse
def index(request):
# code here
if ...:
next = reverse("raison", kwargs={"individu_id": individu_id})
return HttpResponseRedirect(next)
or - even easier - just use the redirect shortcut:
from django.shortcuts import redirect
def index(request):
# code here
if ...:
return redirect("raison", individu_id=individu_id)
There might be other issues with your code but from the infos you posted, those hints should at least put you back on tracks.
Given the following situation:
# models.py
class Book(Model):
pass
# views.py
class BookDetail(DetailView):
model = Book
# books/urls.py
urlpatterns += [path('detail/<int:pk>', BookDetail.as_view(), 'book_detail')]
# page/urls.py
urlpatterns += [path('books/', include('books.urls'))]
I can load the detail view for the object with the private key id 42 at /books/detail/42/. If I am in another request with a completely different path and hold a reference to the object with the private key id 42, is there an "official" or builtin way to generate the url /books/detail/42/? Preferably outside of templating, so I can respond with a JSON.
Or is the idiomatic way to parametrize the path elements (books and detail) and just rebuild it myself?
Yes, you can make use of get_absolute_url for model-specific views and the {% url ... %} template tag to calculate the name of a view.
Model-specific views
If a Model has a specific view to show details, you can implement a get_absolute_url [Django-doc] on your model, like:
from django.urls import reverse
class Book(Model):
def get_absolute_url(self):
return reverse('book_detail', kwargs={'pk': self.pk})
Here we use reverse [Django-doc] to "calculate" the URL for the given name of the view (specified in your path(..., name='book_detail')) with as pk parameter, the pk of self.
In your template you can then write:
{{ my_object }}
with my_object the name of the variable in your template.
It is note that the redirect [Django-doc] function understands the get_absolute_url, and thus you can write return redirect(my_object) in a view, and it will automatically call get_absolute_url to redirect to the proper view.
If you serialize a model with the Django REST framework, then you can reuse the get_absolute_url as well, by using a URLField [drf-doc] for example:
from rest_framework.serializers import ModelSerializer, URLField
class BookSerializer(serializers.ModelSerializer):
absolute_url = serializers.URLField(
read_only=True,
source='get_absolute_url'
)
This specific use case is documented in the documentation of the Django REST framework.
Making use of {% url ...%} in the template
You can also calculate the URL of a view, by using the {% url ... %} template tag [Django-doc]. You can for example write:
{{ my_object }}
to calculate the URL, just like we did with the get_absolute_url. It is however useful as well for other, non model-specific views.
You should use Django Rest Framework
All built-in, you just have to set up.
in my URL for the createview, I want there to be a '?', from where I can pass an argument to the nect page. I am using class based views. For example:
www.site.com/appname/appointment/add/?Name=1
And my HTML would be:
href={% url 'People:appointment-create' Patient.id %}
Currently my URL is like so:
re_path(r'appointment/add/$', views.appointmentCreate.as_view(), name='appointment-create'),
and my view is:
class appointmentCreate(LoginRequiredMixin, CreateView):
model = appointment
form_class = AppointmentForm
def get_initial(self):
patient = self.request.GET.get('patient')
return {
'Patient': patient,
}
How would i go about doing this?
You can try something like this:
href={% url 'People:appointment-create' %}?patient_id={{ Patient.id }}
(so just pass the query arguments after the url as normally).
Also, if you want a more general solution on this problem (automatically generate the form's initial values from query parameters) take a look at this section https://spapas.github.io/#configure-the-form-s-initial-values-from-get-parameters from by article on CBVs.
So here's the basics:
first of all I would like to let you know about below line you just wrote:
{% url 'People:appointment-create' Patient.id %}
to match above url you will need to include below url:
path(r'appointment/add/<int:patient>', views.appointmentCreate.as_view(), name='appointment-create'),
This is the difference between request parameters and kwargs here you pass patient id as kwargs and access in Class base view in self.kwargs
The constructed url will be like below:
www.site.com/appname/appointment/add/1/
Next thing is that if you want to post request parameter (which don't require to add any additional url in your urls.py) you can use as below:
href="{% url 'People:appointment-create' %}?patient={{Patient.id}}"
Note that the parameter you use here will be available in view if you use name in request parameter then you need to access it as self.request.GET.get('name') and if you want to use patient as request parameter then you can access it as self.request.GET.get('patient').
In the below code i am trying to send a object with the request,Is this correct if so how to decode it in template
def index(request):
cat = Category.objects.filter(title="ASD")
dict = {'cat' : cat}
request.update('dict' : dict)
#or
request.dict=dict;
And In the templates can we write the code as
{% for obj in request.dict%}
obj.title
{% endfor %}
EDIT:
If i am calling function like
def post_list(request, page=0, paginate_by=20, **kwargs):
logging.debug("post_list")
page_size = getattr(settings,'BLOG_PAGESIZE', paginate_by)
return list_detail.object_list(
request,
queryset=Post.objects.published(),
paginate_by=page_size,
page=page,
**kwargs
)
You could do this, but why would you want to? Django has a simple, well-defined and well-documented way of passing data into templates - through the context. Why try and find ways to work around that?
Edit after comment No. Again, Django has a perfectly good way of passing extra context into a generic view, via the extra_context parameter which again is well-documented.
You're not showing the actual function you use to render your view (render(), render_to_response(), etc.).
Let's say you are using render_to_response() :
render_to_response(template_name[, dictionary][, context_instance][, mimetype])
Renders a given template with a given
context dictionary and returns an
HttpResponse object with that rendered
text.
So if you pass in {"foo": your_object} as a dictionary you can use {{ foo }} directly in your template.
If you are using the object_list generic view you should use the extra_context:
extra_context: A dictionary of values
to add to the template context. By
default, this is an empty dictionary.
If a value in the dictionary is
callable, the generic view will call
it just before rendering the template.
{% include 'django.contrib.auth.views.login' %}
I don't want to write everything by hand.. I hate this really, django full of automatic stuff.
Goal is to include registration/login.html into base.html, so that I could have this form in every page
If I include only template itself (registration/login.html), problem appears that "form.login", I mean "form" var is not defined because this one comes from VIEW which called when you going to login url. So how can I call that view MANUALLY with include or at least to grab django.contrib.auth.views.login variables by my self in my own view and pass then to base.html?
P.s. It's not just about login form, I think there will be more situations like this
I have found better solution in #django irc.
They called inclusion tags
I'll give you my code, because I got lot's of problem learning new stuff in django =)
file: templatetags/form_login.py
from django import template
register = template.Library()
from django.contrib.auth.forms import AuthenticationForm
#register.inclusion_tag('registration/login.html')
def form_login():
return { 'form': AuthenticationForm() }
Now you can have your form anywhere, this will prerender template and THAT'S IT! no stupid context processors which requires to modify whole project settings.py, which is really sux if you writing stand alone little application..
If you need login-form on every page
Create a context processor:
def login_form_processor(request):
return {
'login_form': LoginForm(request.POST or None)
}
Add it to settings.CONTEXT_PROCESSORS.
Include the template for login form:
{% with login_form as form %}
{% include "registration/login.html" %}
{% endwith %}
You can also make you form lazy-loading, so form will not be created until it is used for the first time.
from django.utils improt functional
def login_form_processor(request):
create_login_form = lambda: LoginForm(request.POST or None)
return {
'login_form': functional.lazy(create_login_form, LoginForm)
}
But I guess you won't want the lazy-loading feature, because login-form is cheap to initialize.
Reusing views
Concerning the "grabbing variables" part from your question: you cannot grab variable from view. Django view is method which returns response object. You can not get variables from response. However some of views accept extra_context and other attributes. Those attributes allow you to configure those views in urls, or to wrap them with your own view, for example:
def my_login_view(request):
some_extra_data = get_some_data()
extra_context = {
'some_extra_var': some_extra_data
}
return login_view(request, extra_context=extra_context, template="my_template.html")
This is not exactly grabbing the variables from views, more like augmentation of existing views.
If you expect to have more situations like this, do less data-porcessing in views. Call some methods which checks for permissions. Collect some data from context-processors. Return rendered response. Now you can reuse the data in other views.
You can specify the action on the form html to point to the URL that accesses the corresponding view.
If you want a form, say called as login_form always populated in all templates, then put it in the context_processors.
Browsing the code for django.contrib.auth.views, you will see that the variables form, site and *site_name* are passed to the template.
Either you (1) provide your custom registration form or (2) you can just import django.contrib.auth.forms.AuthenticationForm in your view if you want to use it.