Django referencing urls between apps - django

I have a project which has just assimilated several apps from other projects of mine to one main project. The structure is one main app that lies at the root url and other apps with specific urls. I have set up my urls.py:
url(r'^$', include('main_app.urls')),
url(r'^app1/', include('app1.urls')),
url(r'^app2/', include('app2.urls')),
url(r'^app3/', include('app3.urls')),
I have a model in my main_app models.py which describes my other apps along the lines of:
class App(models.Model):
title = models.CharField()
image = models.ImageField("App Image", upload_to="images/apps/", blank=True, null=True)
description = models.TextField()
And in my main_app views:
def index(request):
app_list = App.objects.all()
return render_to_response('index.html',
locals(), context_instance=RequestContext(request))
So, in my root index template (main_app) I want to cycle through all apps and print a link:
{% for app in app_list %}
{{ some_app_variables }}
Link to app
{% endfor %}
My question is how should I define this link. Should I have a get_absolute_url for the app model?
Any help much appreciated.

An app doesn't inherently have some URL associated with it. URLs are tied to views, and those views could reference any model from any app or no models or apps at all for that matter. What's you're doing doesn't make sense.
UPDATE: I'm still unsure about what you're linking to. An "app" is an abstract concept. I understand having a list of "Apps", but what kind of view would you get for an individual app? Still, yes, you should have a get_absolute_url method on your Apps model. Then, your best bet would be to name whatever view in each app is going to be the "index" view something along the lines of "app_(title)". Then you can construct the right reverse with something along the lines of:
#models.permalink
def get_absolute_url(self):
return ('app_%s' % self.title, (), {})
Of course, you should probably modify that with something akin to a slug to accommodate titles that might have multiple words, e.g. "Cool App" would need to be replaced with something like "cool_app".
Like I said, though, what you're trying to accomplish still doesn't make much sense from where I'm sitting.

Related

How can I use the blog index within the homepage?

I'm new to wagtail and pretty new to django. I'm wondering how to implement the blog that's documented here:
https://docs.wagtail.io/en/stable/getting_started/tutorial.html
but directly within the home page. Meaning, I'd like the blog index to be the root of the site (like most blogging sites have).
Thanks in advance!
You could just add your blog 'posts' (e.g. BlogPage) as direct children under the home page.
This will mean your blog page URLs will be directly under the root URL.
e.g. mydomain.com/my-cool-post/.
Note: Other pages under the home page will share this route area (e.g. /contact-us/).
Essentially just follow the steps in the tutorial but ignore the part about the BlogIndex. Keep your BlogPage model as is and when adding children in the admin UI add them under the home page.
If you want to list all the posts on your HomePage template, you can revise the template context to return blog_pages similar to the documentation.
You can filter a page queryset by type using exact_type. Or, as shown below, you can use BlogPage.childOf(...) to query the other way.
Django documentation about the queryset api.
my-app/models.py
class HomePage(Page):
body = RichTextField(blank=True)
content_panels = Page.content_panels + [
FieldPanel('body', classname="full"),
]
def get_context(self, request):
context = super().get_context(request)
# Add extra variables and return the updated context
# note: be sure to get the `live` pages so you do not show draft pages
context['blog_pages'] = BlogPage.objects.child_of(self).live()
return context
my-app/templates/home_page.html
{{ page.title }}
{% for blog_page in blog_pages %}
{{ blog_page.title }}
{% endfor %}
Simple, using redirect in urls.py like below code:
from django.views.generic import RedirectView
urlpatterns = [
path(r'^$', RedirectView.as_view(url='/blog/', permanent=False)),
# pass other paths
]

Embed an optional Django application in another page, if that app is present

We have a Django project installed at multiple sites. At a couple of them, there will also be an app which produces a status box which should show on the front page, say. What's the right way to have it show up, if the app happens to be installed. If the app is not present, then nothing should display.
I think I could do it by having the status app extend the main index page:
{% extends "mainproject/index.html" %}
{% block statusboxplaceholder %}
<div> status here </div>
{% endblock %}
Is that the right, idiomatic approach? It seems slightly counterintuitive to extend the entire front page just to add a little bit of content.
EDIT: Also, how do I manage the fact that my app will want to define its own "index" page, that should be shown in preference to the project-wide "index" page? I obviously don't want to hard-code a reference to it in the project's urls.py. Do I create a deployment-specific urls.py which refers to specific apps that are installed at that deployment? And if so, isn't that repeating the information in INSTALLED_APPS, and hence violating DRY?
Although I don't see a problem with your approach, but I think a generic template tag would provide the most flexibilty, especially if you want to extend this ability to other applications that you might install later.
Your base template loads a generic "boxes" tag. In the tag's source, you can then render whatever you want based on the installed apps for that particular instance. So you can have a set of default apps to render boxes for, or the end user can customize which apps should have boxes rendered.
In your settings, configuration or even in the tag itself, you can identify the template to render for the block per app.
Assuming that each app has its templates in app/templates directory - this psuedo should get you going (this is untested):
from django.conf import settings
from django import template
register = template.Library()
class GenericBox(template.Node):
def __init__(self, app):
self.app = app
def render(self, context):
if self.app not in settings.INSTALLED_APPS:
return '' # if the app is not installed
# Here you would probably do a lookup against
# django.settings or some other lookup to find out which
# template to load based on the app.
t = template.loader.get_template('some_template.html') # (or load from var)
c = template.RequestContext(context,{'var':'value'}) # optional
return t.render(c)
#register.tag(name='custom_box', takes_context=True, context_class=RequestContext)
def box(parser, token):
parts = token.split_contents()
if len(parts) != 2:
parts[1] = 'default_app' # is the default app that will be rendered.
return GenericBox(parts[1])

django admin: separate read-only view and change view

I'd like to use the django admin to produce a read-only view of an object which contains an "Edit" button which switches you to the usual change view of the same object.
I know how to use the readonly attributes to produces a read-only view, but I don't know how to produce two views, one read-only and one that allows changes.
I'd like to reuse as much of the admin interface for this as possible, rather than writing a view from scratch.
Note that this question isn't about permissions: all users will have permission to change the objects. It's just that I would prefer that they not use the change_view unless they do intend to make changes, reducing the risk of accidental changes or simultaneous changes.
Here's an answer that literally does what I asked with only a few lines of code and just a couple of template changes:
class MyModelAdmin(admin.ModelAdmin):
fieldsets = [...]
def get_readonly_fields(self, request, obj=None):
if 'edit' not in request.GET:
return <list all fields here>
else:
return self.readonly_fields
Now the usual URL for the change_form will produce a read only change_form, but if you append "?edit=1" to the URL, you will be able to edit.
The change_form template can also be customized depending on whether "?edit=1" is in the URL. To do this, put 'django.core.context_processors.request' in TEMPLATE_CONTEXT_PROCESSORS in settings.py, and then use request.GET.edit in the template.
For example, to add an "Edit" button when not in edit mode, insert
{% if not request.GET.edit %}
<li>Edit</li>
{% endif %}
just after <ul class="object-tools"> in change_form.html.
As another example, changing change_form.html to contain
{% if save_on_top and request.GET.edit %}{% submit_row %}{% endif %}
will mean that the submit row will only be shown in edit mode. One can also hide the Delete buttons on inlines, etc, using this method.
For reference, here is what I put in settings.py:
TEMPLATE_CONTEXT_PROCESSORS = (
'django.contrib.auth.context_processors.auth',
'django.core.context_processors.debug',
'django.core.context_processors.i18n',
'django.core.context_processors.media',
'django.contrib.messages.context_processors.messages',
# Above here are the defaults.
'django.core.context_processors.request',
)
I'd suggest to reconsider using custom views. With the help of generic DetailView, you'll need to write literally two lines of code. The template won't require much work either. You just extend standard change_form.html template, overriding field_sets block.
I know how to use the readonly attributes to produces a read-only view, but I don't know how to produce two views, one read-only and one that allows changes.
You actually can register one model in the admin twice[1], using proxy models. (There're some inconsistencies with permissions for proxy models, but it may not be a problem in your case.)
It seems to be possible to register multiple admin sites[2], too.
I'd like to reuse as much of the admin interface for this as possible, rather than writing a view from scratch.
Interface reuse as such has little to do with views, being mostly template- and style-related thing. View, however, should provide the template context necessary for interface reuse, as you correctly pointed out.
If you decide to go with multiple views per one ModelAdmin, then it might be useful for you to check how django-reversion project implements its admin integration: reversion/admin.py.
References
[1] Multiple ModelAdmins/views for same model in Django admin
[2] Registering Multiple Admin Sites
You will need to change template django admin uses for model form. Make it readonly and add a button to original template linked to another url.
Note:
I highly discourage this approach, you will definitely not prevent simultaneous changes. This should be solved with locking.
Also, I recommend using django-reversion for keeping history of objects and eliminating "accidental changes" risk.
You could create a custom view and display your object there.
To create a custom view in an admin module, override the get_urls() method :
class MyAdmin(admin.ModelAdmin):
…
def get_urls(self):
urls = super(MyAdmin, self).get_urls()
my_urls = patterns('',
url(r'^custom_view/(?P<my_id>\d+)/$', self.admin_site.admin_view(self.custom_viem), name='custom_view')
)
return my_urls + urls
def custom_view(self, request, my_id):
"""Define your view function as usual in views.py
Link to this view using reverse('admin:custom_view')
"""
from myapp import views
return views.custom_view(request, my_id, self)
In views.py :
def custom_view(request, object_id, model_admin):
admin_site = model_admin.admin_site
opts = model_admin.model._meta
my_object = get_object_or_404(MyObject, pk=object_id)
# do stuff
context = {
'admin_site': admin_site.name,
'opts': opts,
'title': _('My custom view'),
'root_path': '%s' % admin_site.root_path,
'app_label': opts.app_label,
'my_object': my_object,
}
return render_to_response('my_template.html', context,
context_instance=RequestContext(request))
In your template, use {% extends "admin/base_site.html" %} to keep the admin look and feel.
The below code is implementation of read-only admin using proxy models.
Models.py
//real model
class CompetitionEntry(models.Model):
pass
//Proxy model
class ReviewEntry(CompetitionEntry):
class Meta:
proxy = True
def save(self, *args, **kwargs):
pass
admin.py
//editable admin
class CompetitionEntryAdmin(admin.ModelAdmin):
pass
admin.site.register(CompetitionEntry, CompetitionEntryAdmin)
// read-only admin (assign only "change" permission for this)
class ReviewEntryAdmin(admin.ModelAdmin):
pass
admin.site.register(ReviewEntry, ReviewEntryAdmin)

Minimal Url and view for static pages on django

The app I am creating has many static pages, just like the pages of a website which do not change for some time. Im my model I will have a title field and a text field. I am looking go a way to avoid multiple views and multiple urls for each page. I tried using flatpages, but I was not able to get to work the context processors. For example, if a certain page has many grids. So how to write a single view and a url which will deliver all the pages, along with the context processors.
If you are having problems with flatpages, it's not hard to write your own version!
models.py
from markdown import markdown
class CustomFlatPage(models.Model):
title = models.CharField(max_length=100)
body = models.TextField()
slug = models.SlugField(unique=True)
urls.py
url(r'^(?P<slug>[-\w]+)/$','myapp.views.customflatpage', name='customflatpage'),
views.py
from django.views.generic.simple import direct_to_template
def customflatpage(request,slug):
page = get_object_or_404(CustomFlatPage, slug=slug)
return direct_to_template('path/flatpage_template.html',
extra_context={
"page":page,
})
template (has access to request context etc.)
{{ page.title}}
{{ load markup }}
{{ page.body|markdown }}
Use TinyMCE on the body field in django-admin (if you are using it) or your own form if you want rich text.

Django Dynamic menu design question

I want to create dynamic menus according to user permissions. As was already discussed here and by the the documentation itself, I know that I can achieve this in the templates using the following snippet:
{% if perms.polls.can_vote %}
<li>
Vote
</li>
{% endif %}
But the problem is that for security reasons I want to limit the access to the views too. The snippet that I found in the documentation is the following:
from django.contrib.auth.decorators import permission_required
def my_view(request):
# ...
my_view = permission_required('polls.can_vote', login_url='/loginpage/')(my_view)
Isn't this against DRY principle? Isn't there a way to define only in one place what is the permission needed for each url? Perhaps in urls.py?
EDIT: (See end of post for the original text of the answer with the initial, simple idea.)
After being kindly stricken with a cluebat (see the OP's comment below), I find I can see more to the problem than before. Sorry it took so long. Anyway:
Would this kind of template be alright for you?
{% for mi in dyn_menu_items %}
{% if mi.authorised %}
<a href={{ mi.url }}>{{ mi.title }}</a>
{% endif %}
{% endfor %}
To make this work on the Python side, you could use RequestContext in your views with a custom context processor setting the dyn_menu_items variable appropriately. In case some background information is required, the Advanced Templates chapter of the Django Book introduces RequestContext, shows how to use it with render_to_response (kinda important :-)) etc.
Also, I guess at this point it could be useful to put the view functions responsible for the locked-up sections of your site in a list somewhere:
_dyn_menu_items = [(url1, view1, title1, perm1), ...]
Then you could map a couple of functions, say prepare_pattern and prepare_menu_item across that list, having it work roughly like so:
def prepare_pattern(menu_item):
url1, view, title, perm = menu_item
pattern = PREPARE_URLCONF_ENTRY_SOMEHOW(...) # fill in as appropriate
return pattern
def prepare_menu_item(menu_item):
url, view, title, perm = menu_item
mi = PREPARE_THE_BIT_FOR_REQUESTCONTEXT(...) # as above
return mi
These could be combined into a single function, of course, but not everybody would find the result more readable... Anyway, the output of map(prepare_menu_item, _dyn_menu_items) would need to be a dictionary to be passed to your views by a helpful context processor (the figuring out of which, it being the slightly tedious bit here, I'll leave to you ;-)), whereas the output of map(prepare_pattern, _dyn_menu_items), let's call it dyn_menu_patterns, would be used in patterns('', *dyn_menu_patterns), to be used in your URLconf.
I hope this makes sense and is of some help...
THE PRE-EDIT ANSWER:
Based on your short description, I'm not sure what solution would be best for you... But if the permission_required snippet does what you want, just not DRY-ly enough, how about rolling your own wrapper:
def ask_to_login(perm, view):
return permission_required(perm, login_url='/loginpage/', view)
You could put this anywhere, including in URLconf. Then you could replace all mentions of '/loginpage/' with reference to a variable defined towards the top of your URLs file and you'd have yourself a solution with a single mention of the actual login URL, for one-place-only update of said URL should you have to move it around. :-)
Of course the views would still need to be wrapped explicitly; if that bothers you, you could try to make ask_to_login into a decorator for easy wrapping at the definition site. (But perhaps it's really best not to do it, lest you force yourself to dig your views from under the decorator in case you need them undecorated at some point in the future.)
I'm aware this question was asked a couple of weeks ago now, but you mentioned http://code.google.com/p/greatlemers-django-tools/ in one of your comments so I thought I'd chip in.
The project is still active (although it's slightly on the backburner at the moment) but I'm not sure if it is as DRY as you're after. You would still have to specify permissions twice, once in the model object for the menu item and once on the view. This isn't necessarily a bad thing however as the permissions that you define on the menu item may be slightly different from those on the view.
If you wanted to do everything in one place I'd probably suggest a combination of a utility function for use in urls.py that can add restrictions to a view whilst also storing said restriction somewhere for use with a special template tag. I'd imagine it may look something like this.
# Stored in a file named access_check_utils.py say.
from django.conf.urls.defaults import url
from django.core.urlresolvers import get_callable
from django.contrib.auth.decorators import permission_required
access_checked_urls = {}
def access_checked_url(regex, view, kwargs=None, name=None, prefix='', perms=None, login_url=None):
if perms is None:
perms = []
callback = None
if callable(view):
callback = view
elif isinstance(view, basestring):
if prefix:
view_path = "%s.%s" % (prefix, view)
else:
view_path = view
try:
callback = get_callable(view_path)
except:
callback = None
if callback is not None:
# Add all the permissions
for perm in perms:
callback = permission_required(perm, login_url=login_url)(callback)
if name is not None:
access_checked_urls[name] = perms
else:
callback = view
return url(regex, callback, kwargs=kwargs, name=name, prefix=prefix)
That should work for the pit needed in urls.py called the same way as you would with a normal url but with the added perms and login_url parameters (perms should be a list of all the relevant ones).
# In a templatetag folder somewhere
from django import template
from django.core.urlresolvers import
# This needs to point to the right place.
from access_check_utils import access_checked_urls
register = template.Library()
#register.inclusion_tag("access_checked_link.html", takes_context=True)
def access_checked_link(context, title, url, *args, **kwargs):
perms = access_checked_urls.get(url, [])
if not perms:
allowed = True
else:
allowed = context.request.user.has_perms(perms)
return { 'allowed': allowed,
'url': reverse(url, *args, **kwargs),
'title': title }
This would have an associated template file like:
{% if allowed %}{{ title }}{% endif %}
I've not tested this fully, but it should work (or at least be a good basis for something that should work). I'll probably even look to adding something like this into gdt_nav allowing it to check for these base permissions if they exist, and then checking for any extras added.
Hope this is of some help.
--
G