How to read URL parameters in a Wagtail model? - django

I am trying to read parameters from the URL in a Wagtail model function. However, the "request" parameter is not accepted in my model function. I also tried creating a fake function that doesn't read anything, just returns a constant to test that everything else was working fine. This fake function works properly but, as soon as the "request" parameter is added, the function value is not displayed. The problem is without request object, I can't get URL parameters. What am I missing? Do I have to import something for the request parameter to work in the model's functions? Why can't I use the request object in a models function?
example of the URL would use:
http://localhost/blog/?parameter=someValue
MODELS.PY CONTENT
from wagtail.core.models import Page
from wagtail.core.fields import RichTextField
class BlogIndexPage(Page):
intro = RichTextField(blank=True)
content_panels = Page.content_panels + [
FieldPanel('intro', classname="full")
]
def read_parameter(self, request):
my_param = request.GET.get('parameter')
return my_param
HTML TEMPLATE CONTENT
{% block content %}
<h1>My page</h1>
<p>This should be the parameter: {{ page.read_parameter }} </p>
{% endblock content %}
EXPECTED OUTCOME:
My Page
This should be the parameter: someValue
ACTUAL OUTCOME
My Page
This should be the parameter:
MODELS.PY WITH THE FAKE FUNCTION ADDED
class BlogIndexPage(Page):
intro = RichTextField(blank=True)
content_panels = Page.content_panels + [
FieldPanel('intro', classname="full")
]
def read_parameter(self, request):
my_param = request.GET.get('parameter')
return my_param
def fake_function(self, request):
# this function works as long as the 'request' parameter is not included
# Once I add the request parameter, it stops working
return "fake_value"
HTML TEMPLATE FOR THE FAKE FUNCTION
{% block content %}
<h1>My page</h1>
<p>This should be the parameter: {{ page.read_parameter }} </p>
<br>
<p>Displaying fake value: {{ page.fake_function }} </p>
{% endblock content %}
OUTPUT WITHOUT REQUEST PARAMETER IN FAKE FUNCTION (fake value is displayed)
My page
This should be the parameter:
Displaying fake value: fake_value
OUTPUT WITH REQUEST PARAMETER IN FAKE FUNCTION (fake value is not displayed)
My page
This should be the parameter:
Displaying fake value:

The request object needs to be specifically sent to any method in the class requiring it.
It's a redundant function though since the query string parameters are already present in the template via the request context variable.
{{ request.GET.some_parameter }}
If you want to process that parameter you can use a template tag to return a value.
{% get_something as some_results %}
{% for result in some_results %}
...
{% endfor %}
With template tag:
#register.simple_tag(takes_context=True)
def get_something(context):
parameter = context.request.GET.get('some_parameter', None)
if parameter:
return get_some_results(parameter)
else:
return None
Alternatively, override the model's get_context() method and do the work there:
class SomeModel(Page):
...
def get_context(self, request, *args, **kwargs):
context = super().get_context(request, *args, **kwargs)
some_parameter = request.GET.get('some_parameter', some_default_val)
context['something'] = do_something(some_parameter)
return context
In the template you can access the data with {{ something }}

Related

Pass other object/data into Flask Admin model view edit template

I'm extending the edit template for a ModelView so that I can show some other information from the database that is relevant for determining how to edit the record in this view. I know how to extend the template and get it to work, but I can't figure out how to query an object and use it in the template.
Also I need to use the value from the model/record in querying the new object I need to pass.
Here is my code from init.py:
class MilestoneView(ModelView):
edit_template = '/admin/milestone_model/milestone_edit.html'
can_delete = True
#i need something like this to work:
referrals = Referral.query.filter_by(email=model.email)
#then i need to pass referrals into the template
admin = Admin(app, name="My App", template_mode='bootstrap3')
admin.add_view(MilestoneView(Milestone, db.session, name='Milestones'))
Then from milestone_edit.html, I want something like this to work:
{% extends 'admin/model/edit.html' %}
{% block body %}
{{ super() }}
{% for r in referrals %}
<p>{{ r.name }}</p>
{% endif %}
{% endblock %}
But of course the referrals object is not available to use in the template. How do I customize this ModelView in order to pass this object in from the init file? I've reviewed the available posts on this subject(ish) on here and haven't found an answer. Thanks in advance.
Override your view's render method, see code on Github, and test if the view being rendered is the edit view. Now you can inject any data into the kwargs parameter. For example:
class MilestoneView(ModelView):
def render(self, template, **kwargs):
# we are only interested in the edit page
if template == 'admin/model/milestone_edit.html':
# Get the model, this is just the first few lines of edit_view method
return_url = get_redirect_target() or self.get_url('.index_view')
if not self.can_edit:
return redirect(return_url)
id = get_mdict_item_or_list(request.args, 'id')
if id is None:
return redirect(return_url)
model = self.get_one(id)
if model is None:
flash(gettext('Record does not exist.'), 'error')
return redirect(return_url)
referrals = Referral.query.filter_by(email=model.email)
kwargs['referrals'] = referrals
return super(MilestoneView, self).render(template, **kwargs)
Note how the model is retrieved. This is a direct copy of the code in method edit_view code. Adjust the code for your use-case.
Use the variable referrals in your edit Jinja2 template.
The render method is called in the following routes for each view:
'/' - i.e. the list view code
'/new/' - code
'/edit/' - code
'/details/' - code

Pass context through multiple nested inclusion tags in Django

I'd like to pass the 'context' variable through multiple inclusion tags in Django like so:
base.html:
{% load extras %}
{% table_of_contents course %}
table-of-contents.html:
{% load extras %}
<h1>Table of contents</h1>
{% for section in course.sections %}
{% display_section section %}
{% endfor %}
extras.py:
#register.inclusion_tag('table-of-contents.html', takes_context=True)
def table_of_contents(context, course):
return {
'course': course,
}
#register.inclusion_tag('display_section.html', takes_context=True)
def section_expanded(context, section):
# Identify the user from the context request
user = context['request'].user
return {
'section': section,
'completed': section.has_been_completed_by(user),
'outstanding_modules': section.get_outstanding_modules_for(user)
}
However, when I run the code above, I get a key error because the context variable is not passed through to the second inclusion tag:
KeyError at /courses/pivottables-video-course/table-of-contents/
'request'
How can I ensure that the context variable persists when passed through to multiple nested inclusion tags?
You're defining new context with return {'foo': 'bar'} for your templates – and this new context doesn't contain request key. By default context['request'] is set by request context processor (https://docs.djangoproject.com/en/dev/ref/templates/api/#django-template-context-processors-request).
If you want to pass context['request'] through multiple tags, you can do this:
#register.inclusion_tag('table-of-contents.html', takes_context=True)
def table_of_contents(context, course):
return {
# ...
'request': context.get('request'),
# ...
}
#register.inclusion_tag('display_section.html', takes_context=True)
def section_expanded(context, section):
# Identify the user from the context request
user = context['request'].user
return {
# ...
'request': context.get('request'),
# ...
}

Django redirect from one page to another

I am working on a project in Django and I'm facing an issue while redirecting from one page to another on the click of a link. No matter what all I've tried, I end up having a url like:
localhost:8080/page1/page2
instead of moving from localhost:8080/page1 to localhost:8080/page2
I've tried by using HttpResponseRedirect(url)
The recommended way is to use {% url 'url-name' arg1 arg2 kwarg='foo' %} in django template.
You shouldn't hardcode urls in your template but use url names.
More details: https://docs.djangoproject.com/en/1.9/ref/templates/builtins/#url
The equivalent in python code is django.utls.reverse which returns te absolute url or django.shortcuts.redirect which is equivalent to HttpResponseRedirect(reverse('url_name'))
https://docs.djangoproject.com/en/1.10/ref/urlresolvers/#django.urls.reverse
EDIT #1
Use database to pass items between views.
models.py
from django.db.models import Model
class Item(Model):
# your model fields
views.py
def list_view(request):
items = Item.objects.all()
context = {'items': items}
return render(request, 'list_template.html', context)
def details_view(request, item_id):
item = Item.objects.get(id=item_id)
context = {'item': item}
return render(request, 'details_template.html', context)
urls.py
urlpatterns = [
url(r'^/list/$', views.list_view, name='list')
url(r'^/details/(?P<item_id>[0-9]+)/$', views.details_view, name='details'),
]
list_template.html
<!-- your html -->
<ul>
{% for item in items %}
<li>
item number {{ item.id }}
</li>
{% endfor %}
</ul>
<!-- your html -->
{% url ... %} tag produces absolute url to pattern named "details" and substitute part of the address with function argument. In the addres, instead of (?P<item_id>[0-9]+), you'll have item id eg. /details/1/. When you click the link, number 1 is grabbed by regex and passed to the function argument where you can take your Item from the database.

How can I use django-jfu with a ImageField and a FK?

I'm trying to use django-jfu to multiupload images, but I have a problem. I want to handle a foreign key dynamically (via url or something), but I can't think of anything.
I have the following models:
class Event(models.Model):
name = models.CharField(max_length=128)
class Picture(models.Model):
event = models.ForeignKey(Event)
image = models.ImageField(upload_to='media')
According to django-jfu, you have to specify a "upload" view to call from the template via template tag. This is my upload view:
#require_POST
def upload(request):
event = Event.objects.get(id=26)
file = upload_receive(request)
instance = Picture(image = file, event = event)
print instance
instance.save()
basename = os.path.basename(instance.image.path)
file_dict = {
'name' : basename,
'size' : file.size,
'url': settings.MEDIA_URL + basename,
'thumbnailUrl': settings.MEDIA_URL + basename,
'deleteUrl': reverse('jfu_delete', kwargs = { 'pk': instance.pk }),
'deleteType': 'POST',
}
return UploadResponse(request, file_dict)
Right now, as a test, it only saves pictures to event with id=26, but how can I handle it dynamically? This is the view and template where I'm calling the template tag:
view
def add_pictures_to_event(request, event_id):
return render(request, 'add_pictures_to_event.html')
template
{% extends 'base.html' %}
{% load staticfiles %}
{% load jfutags %}
{% block body %}
<div class="container">
<h2>Photo upload</h2>
{% jfu %}
</div>
{% endblock %}
As you can see, the view add_pictures_to_event, gets the request and the id of the event, but I cant seem to pass it to the upload view.
Any help would be appreciated.
I had the same question. I looked at different django versions of jQuery File Upload but stuck with Alem's jfu but with the changes from Thomas Willson to make it work in 1.9. My solution might not be the best but I could not find an other way.
I assume you already created an event and then add images to it.
media_upload_form.html is in my projects static directory. I used the UPLOAD_FORM_EXTRA block to add a hidden formfield with the current event_id:
{% block UPLOAD_FORM_EXTRA %}
<input type="hidden" name="currentevent" value="{{instance.pk}}">
{% endblock %}
I assume you have the view from the docs. I changed in the beginning of the uploadview:
file = upload_receive( request )
event_instance = get_object_or_404(Event, id=request.POST['currentevent'])
instance = Picture( file = file, event=event_instance)
instance.save()
It is probably against all django rules but it works. If anyone has a better solution I like to know too. FormSets maybe?

Django: How to pass object/object id to another template

I am new to the Django web framework.
I have a template that displays the list of all objects. I have all the individual objects listed as a link (object title), clicking on which I want to redirect to another page that shows the object details for that particular object.
I am able to list the objects but not able to forward the object/object id to the next template to display the details.
views.py
def list(request):
listings = listing.objects.all()
return render_to_response('/../templates/listings.html',{'listings':listings})
def detail(request, id):
#listing = listing.objects.filter(owner__vinumber__exact=vinumber)
return render_to_response('/../templates/listing_detail.html')
and templates as:
list.html
{% for listing in object_list %}
<!--<li> {{ listing.title }} </li>-->
{{ listing.title}}<br>
{% endfor %}
detail.html
{{ id }}
The variables that you pass in the dictionary of render_to_response are the variables that end up in the template. So in detail, you need to add something like {'listing': MyModel.objects.get(id=vinumber)}, and then the template should say {{ listing.id }}. But hat'll crash if the ID doesn't exist, so it's better to use get_object_or_404.
Also, your template loops over object_list but the view passes in listings -- one of those must be different than what you said if it's currently working....
Also, you should be using the {% url %} tag and/or get_absolute_url on your models: rather than directly saying href="{{ listing.id }}", say something like href="{% url listing-details listing.id %}", where listing-details is the name of the view in urls.py. Better yet is to add a get_absolute_url function to your model with the permalink decorator; then you can just say href="{{ listing.get_absolute_url }}", which makes it easier to change your URL structure to look nicer or use some attribute other than the database id in it.
You should check the #permalink decorator. It enables you to give your models generated links based on your urls pattern and corresponding view_function.
For example:
# example model
class Example(models.Model):
name = models.CharField("Name", max_length=255, unique=True)
#more model fields here
#the permalink decorator with get_absolute_url function
#models.permalink
def get_absolute_url(self):
return ('example_view', (), {'example_name': self.name})
#example view
def example_view(request, name, template_name):
example = get_object_or_404(Example, name=name)
return render_to_response(template_name, locals(),
context_instance=RequestContext(request))
#example urls config
url(r'^(?P<name>[-\w]+)/$', 'example_view', {'template_name': 'example.html'}, 'example_view')
Now you can do in your templates something like this:
<a href={{ example.get_absolute_url }}>{{ example.name }}</a>
Hope this helps.
In your detail method, just pass the listing into your template like so:
def detail(request, id):
l = listing.objects.get(pk=id)
return render_to_response('/../templates/listing_detail.html', {'listing':l})