Publish a custom Django Flatpage at a set date and time - django

I have a custom Flatpage model:
from django.contrib.flatpages.models import FlatPage
class MyFlatPage(FlatPage):
publish = models.DateTimeField()
so that I can add a publish date in the future.
Now, I don't have a proper list of flatpages on the front end, my use for frontpages is more like 'one-offs', where I specific the URL and all that. For example, 'about', '2019prize', 'Today's walk', stuff like that.
The urls.py is set up to catch all the flatpages with:
from django.contrib.flatpages import views
re_path(r'^(?P<url>.*/)$', views.flatpage)
How can I set these pages I create to be displayed only after the publish date has arrived? I know that I can filter them by looking up something like pages.filter(publish__lte=now). Where and how should I put that code though?
Additional information
I suppose I need to create a custom view, is that correct? The original view is in ../lib/python3.8/site-packages/django/contrib/flatpages/views.py:
def flatpage(request, url)
if not url.startswith('/'):
url = '/' + url
site_id = get_current_site(request).id
try:
f = get_object_or_404(FlatPage, url=url, sites=site_id)
except Http404:
if not url.endswith('/') and settings.APPEND_SLASH:
url += '/'
f = get_object_or_404(FlatPage, url=url, sites=site_id)
return HttpResponsePermanentRedirect('%s/' % request.path)
else:
raise
return render_flatpage(request, f)
#csrf_protect
def render_flatpage(request, f):
if f.registration_required and not request.user.is_authenticated:
from django.contrib.auth.views import redirect_to_login
return redirect_to_login(request.path)
if f.template_name:
template = loader.select_template((f.template_name, DEFAULT_TEMPLATE))
else:
template = loader.get_template(DEFAULT_TEMPLATE)
f.title = mark_safe(f.title)
f.content = mark_safe(f.content)
return HttpResponse(template.render({'flatpage': f}, request))
How can I extend this, adding my if publish__lte=now code?

What I did is copy-paste the view code from ../lib/python3.8/site-packages/django/contrib/flatpages/views.py to my app.views, rename the two functions, and add the following to render_myflatpage:
def render_myflatpage(request, f):
[...]
if f.publish > now:
f.content = 'This content will be published on ' + str(f.publish)
I then assigned the new view in the catch-all urls.py code:
re_path(r'^(?P<url>.*/)$', myflatpage)
I know this goes against the DRY protocol; this works for me for the time being. If there's a more elegant solution please do let me know.

Related

Page view refers to id, whil path is not asking for one

I want to load a default django page. Nothing fancy. However, the error I get, hints at an id that is incorrectly set.
"Field 'id' expected a number but got 'zoekboek'."
The confusing things here (I am a django beginner, so I wouldn't be surprised if this is not confusing at all for you):
the path for this page in the urls.py is not asking for an id.
the view is not querying anything yet (I found some posts that had similar errors,
but related to a filter).
the debug info points to another view that indeed is requesting an id.
when I add a slash at the beginning of the path, the error is gone!
The code
urls.py
urlpatterns = [
path('', views.scholen, name='scholen'),
path('<school_id>', views.school_detail, name='school_detail'),
path('<school_id>/<groep_id>', views.school_groep, name='school_groep'),
path('<school_id>/<groep_id>/<UserProfile_id>', views.leerling_page, name='leerling_page'),
path('zoekboek', views.zoekboek, name='zoekboek'),
]
views.py
from django.shortcuts import render, redirect, reverse, get_object_or_404
from books.models import Book, Rating
from .models import School, Groep
from profiles.models import UserProfile, Hobby, Sport
from django.contrib.auth.models import User
# Create your views here.
def scholen(request):
"""
Homepage for participating
schools.
"""
scholen = School.objects.all()
context = {
'scholen': scholen,
}
return render(request, 'schools/school_landing.html', context)
def school_detail(request, school_id):
"""
Details of individual schools.
"""
school = get_object_or_404(School, pk=school_id)
groep = Groep.objects.filter(school=school)
context = {
'school': school,
'groep': groep,
}
return render(request, 'schools/school_detail.html', context)
def school_groep(request, school_id, groep_id):
"""
Details of groep.
"""
school = get_object_or_404(School, pk=school_id)
groep = get_object_or_404(Groep, pk=groep_id)
a = groep.naam
kinderen = UserProfile.objects.filter(groep=a)
context = {
'school': school,
'groep': groep,
'kinderen': kinderen,
}
return render(request, 'schools/school_groep.html', context)
def leerling_page(request, school_id, groep_id, UserProfile_id):
"""
Personal page of school kids.
"""
profile = get_object_or_404(UserProfile, pk=UserProfile_id)
# If viewer is owner of page, viewer can edit
owner = False
if request.user == profile.user:
owner = True
context = {
'profile': profile,
'owner': owner,
}
return render(request, 'schools/leerling_page.html', context)
def zoekboek(request):
"""
Page for kids to search their favorite book
"""
context = {
}
return render(request, 'schools/zoek_boek.html', context)
Is this enough information?
Simple fix: move path('zoekboek', views.zoekboek, name='zoekboek'), from the last place to the second place in your urls.
Why?
Because Django URLs are resolved using regular expressions; the docs say here in point 3:
Django runs through each URL pattern, in order, and stops at the first one that matches the requested URL, matching against path_info.
Since your URL path path('<school_id>', views.school_detail, name='school_detail'), is very generic, it matches any string including the string zoekboek; so the request to zoekboek falls into the second line in your URL conf and gets routed to the view school_detail() and a school_id is expected for that view.
Suggestion: to make the URL handling easier and so you can order the URL paths however you like, you could change the URL a bit and add a prefix (for example school/) so that not any string matches the URL paths. For example, this schould work:
urlpatterns = [
path('', ...),
path('school/<school_id>', ...),
path('school/<school_id>/<groep_id>', ...),
path('school/<school_id>/<groep_id>/<UserProfile_id>', ...),
path('zoekboek', ...),
]

Django how to pass an id of pagination and model id?

Thankyou i am getting a problem.I am using pagination in url passing a page id and want to pass my model driver id also. if a page is at 1 the url is
*url(r'^rentacar/list/$', extension_views.rentacar_list),*
but after user go to next page the url is:
*url(r'^rentacar/list/(\d+)/$', extension_views.rentacar_list),*
but i actually want to do is to reference driver id in the url that i am trying to pass.
What am trying to do is to get driver id and page# in my url. how do i go about doing this? and how can i change my views and achieve it whereas its running through pagination
Views.py
#csrf_protect
def rentacar_list(request, page_number=1):
all_cars = Car.objects.all().order_by('-id')
if menu_config.menu_item_rentacar_list_show_unavailable == 0:
all_cars = all_cars.exclude(car_available=0)
else:
all_cars = all_cars
cars_page = Paginator(all_cars, menu_config.menu_item_rentacar_list_pagination)
args['cars'] = cars_page.page(page_number)
template = Template.objects.get(template_default__exact=1)
template_page = template.template_alias + str("/rentacar/rentacar_cars_list.html")
return render(request, template_page, args)
Urls.py
url(r'^rentacar/list/$', extension_views.rentacar_list),
url(r'^rentacar/list/(\d+)/$', extension_views.rentacar_list),
but i want to achieve :
url(r'^rentacar/list/driver/id/$', extension_views.rentacar_list),
url(r'^rentacar/list/(\d+)/driver/id/$', extension_views.rentacar_list),
your driver id shouldnt in urlpath,you can transmit data in url parameters,for example:
yourhost/rentacar/list/<page_id>?driver=<driver_id>
get driver_id form request, for example:
driver_id = request.GET.get('driver', None)
last, you can refactor view by django ListView which has buildin pagination function,here is django ListView documnet django listview
example
url.py
url(r'^rentacar/list/(\d+)/$', extension_views.rentacar_list),
views.py
def you_view(request, page_number):
driver_id = request.GET.get('driver', None)
your request url
127.0.0.1/rentacar/list/1?driver=2

Django not displaying correct URL after reverse

There's lots of documentation about Django and the reverse() method. I can't seem to locate my exact problem. Suppose I have two urlconfs like this:
url(r'ParentLists/$', main_page, name = "main_page"),
url(r'ParentLists/(?P<grade_level>.+)/$', foo, name = "foo")
and the two corresponding views like this:
def main_page(request):
if request.method == 'POST':
grade_level = request.POST['grade_picked']
return HttpResponseRedirect(reverse('foo', args = (grade_level,)))
else:
return render(request, 'index.html', context = {'grade_list' : grade_list})
def foo(request, grade_level):
grade_level = request.POST['grade_picked']
parent_list = # get stuff from database
emails = # get stuff from database
return render(request, 'list.html', context = {'grade_list' : grade_list, 'parent_list' : parent_list})
Here, list.html just extends my base template index.html, which contains a drop down box with grade levels. When the user goes to /ParentLists, the main_page view renders index.html with the drop down box as it should.
When the user picks a grade level from the drop down box (say 5th Grade), the template does a form submit, and main_page once again executes - but this time the POST branch runs and the HttpResponseRedirect takes the user to /ParentLists/05. This simply results in an HTML table pertaining to grade 5 being displayed below the drop down box.
The problem is, when the user now selects say 10th Grade, the table updates to show the grade 10 content, but the URL displayed is still /ParentLists/05. I want it to be /ParentLists/10.
Clearly, after the first selection, the main_page view never executes again. Only foo does...and so the HttpResponseRedirect never gets called. How should I reorganize this to get what I'm looking for? Thanks in advance!
As you correctly mentioned you will never redirect to foo() from foo().
So the simple way to fix this is just add similar code as in main_page() view:
def foo(request, grade_level):
if request.method == 'POST':
grade_level = request.POST['grade_picked']
return HttpResponseRedirect(reverse('foo', args = (grade_level,)))
else:
parent_list = # get stuff from database
emails = # get stuff from database
return render(request, 'list.html', context = {'grade_list' : grade_list, 'parent_list' : parent_list})
Please note that I remove grade_level = request.POST['grade_picked'] because as Nagkumar Arkalgud correctly said it is excessively.
Also instead of combination of HttpResponseRedirect and reverse you can use shortcut redirect which probably little easy to code:
from django.shortcuts redirect
...
return redirect('foo', grade_level=grade_level)
I would suggest you to use kwargs instead of args.
The right way to use the view is:
your_url = reverse("<view_name>", kwargs={"<key>": "<value>"})
Ex:
return HttpResponseRedirect(reverse('foo', kwargs={"grade_level": grade_level}))
Also, you are sending "grade_level" to your view foo using the URL and not a POST value. I would remove the line:
grade_level = request.POST['grade_picked']
as you will override the grade_level sent to the method from the url.

Most appropriate way to redirect page after successful POST request in Django

I have build a view and a form in Django1.5. If the POST request is successful (based on some values I set) then I need the page to redirect to another URL which is created simultaneously.
Otherwise, if the POST was not successful I need to stay on the same page. Right now I have solved the problem as following but I am quite sure this is not the best way to do it:
This is a part of my view:
def layer_create(request, template='layers/layer_create.html'):
if request.method == 'GET':
....
elif request.method == 'POST':
out = {}
...
new_table = 'something that comes from the form'
if form.is_valid():
...
try:
...
out['success'] = True
except:
...
out['success'] = False
finally:
if out['success']:
status_code = 200
# THIS IS THE PART WHICH I THINK I CAN IMPROVE
template = '/something/workspace:' + new_table + '/metadata'
else: # if form not valid
out['success'] = False
return render_to_response(template, RequestContext(request, {'form': form}))
This part of the code:
template = '/something/workspace:' + new_table + '/metadata'
seems very ugly to me. But as I am quite new in Django I am not sure how to approach this matter.
A side note first about Django 1.5 - you're highly advised to upgrade to a supported version like 1.8.
Redirecting
For redirecting you can use the redirect shortcut. (Or HttpResponseRedirect)
from django.shortcuts import redirect
# out of a view context
return redirect('/url/to/redirect/to/')
Building URLs
Indeed - as you did mention, your attempt with template = '/something/workspace:' + new_table + '/metadata' is not the cleanest way :)
Django provides a really nice way with the URL dispatcher.
A complete solution here would go too far (or definitely would require more detailed information about your project structure) - I would recommend you to dive into the Django URL dispatcher.
In short you would do something like:
# app/urls.py
urlpatterns = [
#...
url(r'^workspace/(?P<id>[0-9]+)/metadata/$', views.workspace_detail, name='workspace-detail-metadata'),
#...
]
Then you are able to reverse your URL patterns:
from django.core.urlresolvers import reverse
url = reverse('workspace-detail-metadata', kwargs={'id': 123})
# would result in:
# .../workspace/123/metadata/
After all, I have used the "reverse" method as follows:
layer = 'geonode:' + new_table
return HttpResponseRedirect(
reverse(
'layer_metadata',
args=(
layer,
)))
Where my urls.py file includes:
url(r'^(?P<layername>[^/]*)/metadata$', 'layer_metadata', name="layer_metadata"),
As described here this is the most appropriate way to do it.

How to set cookie in Django and then render template?

I want to set a cookie inside a view and then have that view render a template. As I understand it, this is the way to set a cookie:
def index(request):
response = HttpResponse('blah')
response.set_cookie('id', 1)
return response
However, I want to set a cookie and then render a template, something like this:
def index(request, template):
response_obj = HttpResponse('blah')
response_obj.set_cookie('id', 1)
return render_to_response(template, response_obj) # <= Doesn't work
The template will contain links that when clicked will execute other views that check for the cookie I'm setting. What's the correct way to do what I showed in the second example above? I understand that I could create a string that contains all the HTML for my template and pass that string as the argument to HttpResponse but that seems really ugly. Isn't there a better way to do this? Thanks.
This is how to do it:
from django.shortcuts import render
def home(request, template):
response = render(request, template) # django.http.HttpResponse
response.set_cookie(key='id', value=1)
return response
The accepted answer sets the cookie before the template is rendered. This works.
response = HttpResponse()
response.set_cookie("cookie_name", "cookie_value")
response.write(template.render(context))
If you just need the cookie value to be set when rendering your template, you could try something like this :
def view(request, template):
# Manually set the value you'll use for rendering
# (request.COOKIES is just a dictionnary)
request.COOKIES['key'] = 'val'
# Render the template with the manually set value
response = render(request, template)
# Actually set the cookie.
response.set_cookie('key', 'val')
return response
response = render(request, 'admin-dashboard.html',{"email":email})
#create cookies
expiry_time = 60 * 60 #in seconds
response.set_cookie("email_cookie",email,expiry_time);
response.set_cookie("classname","The easylearn academy",expiry_time);
def index(request, template):
response = HttpResponse('blah')
response.set_cookie('id', 1)
id = request.COOKIES.get('id')
return render_to_response(template,{'cookie_id':id})