I have seen similar questions and answers but none that address my problem.
I want my view to perform a User Group check and then pass that via variable to the template. The template will then use that to appear differently to different user groups.
My views.py:
def cans(request):
is_canner = request.user.groups.filter(name='canner') #check if user group = canner
can_list = Can.objects.order_by('name')
context = {'can_list': can_list}
return render(request, 'cans/cans.html', context) #need to return is_canner variable here
And in my template I would use the variable like so:
{% if is_canner %} canner stuff goes here {% endif %}
I'm unsure how to pass this variable, I thought it used context to send it like so:
return render(request, 'cans/cans.html', context({"is_canner": is_canner}))
But this gives me errors - context is not callable.
context is not a function, its an argument to the render function, e.g.
context = {"is_canner": is_canner}
return render(request, 'cans/cans.html', context)
docs: https://docs.djangoproject.com/en/dev/topics/http/shortcuts/#render
more background info: Django - what is the difference between render(), render_to_response() and direct_to_template()?
Related
Using Django I want to implement some middleware that will calculate some context that is to be used by the view itself.
For example, I have a middleware that looks at the request, and adds the user's permissions to the request, or some user configuration. The view looks at these permissions and decides how to handle the request using it.
This saves the need for multiple views (and multiple parts within the view) to query for this information.
I'm wondering what is the correct way to do that. One option is to just add request.user_permissions=... directly on the request. But is there some documented and expected way to do that?
There's no real documented way to do that, but Middleware is the correct place to do it and just adding properties to the request object is also the correct way.
You can confirm this, because Django is already doing it:
LocaleMiddelware
AuthenticationMiddleware
RemoteUserMiddleware
CurrentSiteMiddleware
SessionMiddleware
So just pick whatever is the most convenient data structure for your use case and tack that on to the request object.
This is not a perfect answer but at my experience I use this code. Every permission is saved in a boolean value which is true or false. You can access it in a html template like.
{% if request.user.is_admin %}
"Your code here"
{% else %}
"Your code here"
{% endif %}
and to send extra context you should create and pass an dicionary and pass it as as an argument to the render method from the view.
For eg:
def view(request, slug):
context = {'administrator':True}
blog_post = get_object_or_404(BlogPost, slug=slug)
context['blog_post'] = blog_post
return render(request, 'blog/detail_blog.html', context)
and access it like
{% if context.administrator %}
"Your code here"
{% else %}
"Your code here"
{% endif %}
I believe, since your middleware will calculate context, it should be implemented as context processor.
https://docs.djangoproject.com/en/3.1/ref/templates/api/#using-requestcontext
https://docs.djangoproject.com/en/3.1/ref/templates/api/#writing-your-own-context-processors
I want to display a value that user types
I am able to get the value using this lines:
def on_submit_click(req):
name = req.GET['search']
return render(req, 'search_test.html')
But i'm not quite sure how to display it on the website
I tried doing this in the search_test.html:
<h1 class="text-white text-center">{{ name }}</h1>
But it didn't work either.
You're missing the template context in your invocation of render(). render() takese these parameters:
render(request, template_name, context=None, content_type=None, status=None, using=None)
The template context is a dictionary with values which will replace the placeholders in the template. In your case, you want to replace the {{name}} with the search query param, I assume. So, your method should look like this:
def on_submit_click(req):
name = req.GET['search']
return render(req, 'search_test.html', {"name": name}, content_type='application/xhtml+xml')
I've also set the content type
I'm using Django 2.1.
I'm having a problem with a CreateView because I need to redirect to the update url, but that url contains one argument that is created manually after verifying that the form is valid.
This is the view code:
class ProjectCreateInvestmentCampaignView(LoginRequiredMixin, SuccessMessageMixin, generic.CreateView):
template_name = 'webplatform/project_edit_investment_campaign.html'
model = InvestmentCampaign
form_class = CreateInvestmentCampaignForm
success_message = 'Investment campaign created!'
def get_success_url(self):
return reverse_lazy('project-update-investment-campaign',
args=(self.kwargs['pk'], self.object.campaign.pk, self.object.pk))
def form_valid(self, form):
project = Project.objects.get(pk=self.kwargs['pk'])
form.instance.investment_type = "A"
form.instance.contract_type = "CI"
form.instance.history_change_reason = 'Investment campaign created'
valid = super(ProjectCreateInvestmentCampaignView, self).form_valid(form)
if valid:
campaign = CampaignBase.objects.create(project=project, )
form.instance.campaign = campaign
form.instance.campaign.project = project
form.instance.campaign.creation_date = timezone.now()
form.save()
return valid
As you can see, on the form_valid I validate first the form, and then I create the object campaign and assign all the related data. This is working fine.
The problem came when I changed the get_success_url to fit my use case, that is redirecting to the update view.
I debugged and saw that at the moment I create the variable valid on the form_valid, it checks the success url, and that triggers me the following error:
Exception Type: AttributeError
Exception Value:
'NoneType' object has no attribute 'pk'
Exception Location: /Volumes/Archivos/work/i4b/webplatform/views/investor_campaign_views.py in get_success_url, line 25
I asume that the error is because the campaign is not created yet so it's trying to get the pk from a non existing object.
The thing is that I cannot create the campaign if the form is not validated, but I need the campaign to make the url working (that url is working as it is on the UpdateView that I already have).
It will only invoke get_success_url after form_valid. So it's up to form_valid to create and save the objects needed. If it's valid for them not to be created, you need a different approach. Maybe initialize (say) self.campaign_pk = 0, update it if a campaign can be created with the pk of the campaign object, and let the next view sort out what to do when pk==0. Or,
...
args=(self.kwargs['pk'],
self.object.campaign.pk if self.object.campaign else 0,
self.object.pk))
(I don't fully follow your code so I might be barking up the wrong tree here)
It may be that you don't want CreateView but FormView, which doesn't handle object creation for you, so you may find greater flexibility over how to process a valid form that nevertheless cannot be fully honoured all the time. Or even, just a plain old function-based view, in which you can process two or more forms and be far more able to decide on conditions that constitute non-validity even after all the forms have technically validated.
This is a function-based view structure I have used where I have two forms to process, and a fairly long but boring set of operations to do after BOTH forms validate:
def receive_view( request):
# let's put form instantiation in one place not two, and reverse the usual test. This
# makes for a much nicer layout with actions not sandwiched by "boilerplate"
# note any([ ]) forces invocation of both .is_valid() methods
# so errors in second form get shown even in presence of errors in first
args = [request.POST, ] if request.method == "POST" else []
batchform = CreateUncWaferBatchForm( *args, layout=CreateUncWaferBatchLayout )
po_form = CreateUncWaferPOForm( *args, layout = CreateUncWaferPOLayout, prefix='po')
if request.method != "POST" or any(
[ not batchform.is_valid(), not po_form.is_valid() ]):
return render(request, 'wafers/receive_uncoated.html', # can get this out of the way at the top
{'batchform': batchform,
'po_form': po_form,
})
#it's a POST, everything is valid, do the work
...
return redirect('appname:viewname', ...)
For me, get_success_url was not invoked as the form was not valid (was invalid) and I didn't know. You can override form_invalid(self, form) to control the behavior.
Also, consider this block of code to show any errors in your template
{% if form.errors %}
<div class="alert alert-danger" role="alert">
{% for field, errors in form.errors.items %}
{% for error in errors %}
<b>{{ field }}</b>: {{ error }}
{% endfor %}
{% endfor %}
</div>
{% endif %}
I'm trying to get all attributes of a single object. I keep getting a "Devices matching query does not exist." I just cannot figure out my issue.
Models.py
`class Devices(models.Model):
category_id = models.ForeignKey(Category, on_delete=models.CASCADE)
device_description = models.CharField(max_length=100)
device_status = models.CharField(max_length=50)
device_date = models.DateTimeField()
device_user = models.CharField(max_length=50)`
Views.py
def view_status(request, pk=None):
device = Devices.objects.get(pk=pk)
return render(request, 'homesite/device_status.html', device)
urls.py
url(r'^viewstatus/$', views.view_status, name='ViewStatus'),
here is the url I use to call http://localhost:8000/homesite/viewstatus/?pk=1
device_satus.html
{% extends "base.html" %}
{% block head %}
<title>Device Status</title>
{% endblock%}
{% block body %}
<h3>Device Status Detail</h3>
{{ devices.device_description }}
{{ devices.device_status }}
{{devices.device_date|date:"Y-m-d H:m:s"}}
{% endblock %}
There are 4 records in my able so I know there is a match for PK=1.
Note, that this is not the usual way to build an URL for accessing a specific object. Below I present first the approach that integrates pk in the URI and second the one passing pk as a parameter.
1. Approach
Here you put the pk in the URI and request something like http://localhost:8000/homesite/viewstatus/1/. If you do so, you need to adapt your urls.py by specifying what part of the URI is the pk you want:
# urls.py
url(r'^viewstatus/(?P<pk>\d+)/$', views.view_status, name='ViewStatus'),
The way you wrote the view is fine:
def view_status(request, pk=None):
if pk is not None:
device = Devices.objects.get(pk=pk)
else:
device = None
return render(request, 'homesite/device_status.html', {'device' : device})
Now, views.view_status will be called with both the request object and the pk as arguments and objects.get will behave as you expected, if the pk you put in the URI exists in you database.
Note that this is the preferred way to get an object.
2. Approach
In this case you pass the pk as a parameter, so call http://localhost:8000/homesite/viewstatus/?pk=1, for example. Now pk is a parameter of a GET request. In this case:
# urls.py
url(r'^viewstatus/$', views.view_status, name='ViewStatus'),
And the view only takes the request object as argument. Within the view you can get the pk as follows:
def view_status(request):
pk = request.GET.get('pk', None)
if pk is not None:
device = Devices.objects.get(pk=int(pk))
else:
device = None
return render(request, 'homesite/device_status.html', {'device' : device})
So in this case your view does not take any arguments other than the request object.
Another issue is in your view function: Django's shortcut render takes a dict object for the optional argument context. Currently you directly pass a Devices object. You need to update your return statement in view_status:
return render(request, 'homesite/device_status.html', {'device' : device})
Hope that helps!
I get an error 'Devices' object is not iterable
urls.py
this is how the url is set up.
url(r'^viewstatus/$', views.view_status, name='ViewStatus'),
but is should be like this
url(r'^viewstatus/(?P<pk>\d+)/$', views.view_status, name='ViewStatus'),
so that I can call like this correct? http://localhost:8000/homesite/viewstatus/1/
views.py
def view_status(request):
pk = request.GET['pk']
device = Devices.objects.get(pk=pk)
return render(request, 'homesite/device_status.html', device
so i need the corresponding views.py code to work with
http://localhost:8000/homesite/viewstatus/1/
I've stared at this for hours so I know I'm missing something simple.
Try changing your view function:
def view_status(request):
pk = request.GET['pk']
device = Devices.objects.get(pk=pk)
return render(request, 'homesite/device_status.html', device)
Let me know if it helps :)
I know this has probably been asked a 1000 times but I still for the life of me can't find a decent answer. Reading tutorials, Stack and even looking at GitHub for examples - nothing is helping or even guiding me in the right direction.
I have 2 separate Models/Views/Templates which each work beautifully on their own URLS.
I want these Models/Views/Templates to work on a single URL ie
url(r'^$', 'chrometask.views.index', name='home'),
(ie my homepage.)
How??? This seems to be way overly complicated for such a simple ask.
here is my views.py
items = Task.objects.all()
form = TaskForm(request.POST or None)
task_list = Task.objects.order_by('priority')
context_dict = { 'Tasks' : task_list}
if form.is_valid():
save_it = form.save(commit=False)
save_it.save()
return HttpResponseRedirect('')
return render_to_response('home.html', #Don't know how to render 2 html files or add the context_dict
locals(),
context_instance=RequestContext(request))
render_to_resonse can only take 3 arguments, and when I put the table inside 'home.html' with {% blockquote %} it wont show on the page.
<div class="collapsible-header"><i class="mdi-hardware-laptop-"></i>Title</a></div>
<div class="collapsible-body">
<div class="intro grey lighten-5">
{% block content %}{% endblock %} <--This is the table.html, which extends fine with {% entends 'home.html' %}-->
</br>
Please don't direct me to tutorials, These haven't resolved the issue, I would rather you spelt it out in layman's terms if possible, this may help drill the answer into my thick skull.
note - I am New to Django.
(*apologies for the frankness - this is really beginning to drive me up the wall)
You haven't said what you have tried to do to solve this, why you think it's "way overly complicated", or why it's causing you frustration.
The thing to understand is that a view is entirely responsible for a page, ie the view creates the entire response when a URL is requested. You can't have two views on a URL; that simply doesn't make sense.
But the other thing to understand is that templates and models are not in any way connected to any particular view. One view can render any number of templates, one, many, or even none; and any of those templates can involve any number of models from any number of apps.
So, if you wanted to include tasks in your home page, one approach is just to include the Tasks in the context dictionary in your view, and include the relevant code to your template to display them.
Now, if you're doing this in multiple views, then clearly you want to put that code somewhere sensible so that you Don't Repeat Yourself. The ideal place for this is an inclusion tag: you can wrap up the code that queries the latest tasks, and the template fragment that renders them, into a single tag which you can then use on your home page template.
Edit
The context is the second argument to render_to_response: the place where you've currently put locals(). That's a horrible pattern which I hate, and which is confusing you unnecessarily. Instead of using locals, put all the things you require for the context specifically into context_dict and pass that there instead.
(Also note you should use the render shortcut rather than render_to_response, which accepts the request as the first parameter and doesn't need all that mucking around with RequestContext.)
context_dict = { 'Tasks' : task_list, 'form': form}
...
return render(request, 'home.html', context_dict)
The code you have posted for your template is confusing. Is the table in table.html or home.html? If you pass "home.html" in your call to render, then it's home.html that will be rendered, not table. If you want to render table so that it extends home, then pass "table.html" in your call to render.
My Code ended up looking like
def home(request):
items = Task.objects.all()
form = TaskForm(request.POST or None)
task_list = Task.objects.order_by('priority')
context_dict = { 'Tasks' : task_list, 'form': form}
if form.is_valid():
save_it = form.save()
save_it.save()
return HttpResponseRedirect('')
return render(request, 'table.html', context_dict)
This has both models workig in one view.
def tutlist(request,tut):
model = Tutorial
model = Codetut
template_name = 'backpages/tutorial.html'
temp = Parts.objects.get(pk = tut)
model = temp.tutorial_set.all()
print(model)
temp = Parts.objects.get(pk = tut)
code = temp.codetut_set.all()
print(code)
context = {'model' : model ,'something': code}
return render(request, template_name,context)
i was having a similar problem i wanted to display two different views on a single template with a single url so i did something like this which worked well for me.