Add links to navigation based on INSTALLED_APPS - django

I have a project that has a number of apps. These apps translate into modules that perform different functions for the end user.
Now each deployment of the project may have certain apps enabled or disabled.
What I'd like to achieve is a navigation list of links that only displays links for apps that are in INSTALLED_APPS.
For example deployment 1 has App1 and App2 listed in INSTALLED_APPS so would have a navigation something like this:
Link to App1's view
Link to App2's view
And deployment 2 has App2 and App3 installed so should show a navigation something like this:
Link to App2's view
Link to App3's view
Without having a navigation defined in a base template and editing it for each deployment, I can't see a way of doing this. Even by using { block.super }, this seems to not allow 2 apps to be installed as each child template would append to the parent.

This is strictly from the top of my head, so it may not be the "best" or most appropriate way.
First, if you're going to rely on INSTALLED_APPS you should actually implement something along the lines of:
MY_INSTALLED_APPS = (
'app1',
'app2',
)
INSTALLED_APPS = (
# other installed apps
) + MY_INSTALLED_APPS
Then, instead of having to figure out which of the items in INSTALLED_APPS are yours and which are third-party, you just use MY_INSTALLED_APPS instead for things like building your menus.
Second, in Django, apps aren't tied to views in any meaningful way. There's no concept of a "default" view, and you can't simply link to an app. However, you can some what achieve this idea through the use of namespaces and a convention for view naming, specifically, all your apps will at least have a urlpattern named "index", for example.
Then, in your urls.py you create urlpatterns like:
url(r'^app1/', include('app1.urls', namespace='app1', app_name='app1'),
url(r'^app2/', include('app2.urls', namespace='app2', app_name='app2'),
# etc
In each app, you create a urls.py that has at least one urlpattern:
url(r'^$', some_view, name='index'),
This means that the going to /app1/ in your browser would then load some_view and you can reference this view in your code with a name like: app1:index.
The tricky part is using this in your templates. Django 1.5 will add the ability to use context variables in the {% url %) tag for the view name. As of Django 1.3, you can use this behavior as well via {% load url from future %}. However, even that only gets you part-way to what you need.
In your template, you'll need to loop through the values of MY_INSTALLED_APPS and construct the links. The following should work in Django 1.3-1.4 via {% load url from future %} or Django 1.5:
{% for app in apps %}
{{ app }}
{% endfor %}
The other way to accomplish this is with a template filter, which is your only option in Django <1.3, and may still be preferable in later versions. Something like:
#register.filter
def default_url_for_app(app):
return reverse(app+':index')
And, in your template:
{% for app in apps %}
{{ app }}
{% endfor %}

You can simple import your settings.py file and iterate over the INSTALLED_APPS list.
from django.conf import settings
for installed_app in settings.INSTALLED_APPS:
But I'm also in doubt if this is a smart decision to implement your application as such.

What you really want here is some kind of app registration system. That is, for each app that's loaded (ie included in INSTALLED_APPS), register its homepage with the list of links.
Rather than doing all this automatically, I would follow the example of the admin application and get each app to call a register function on first load. You could do this in the models.py, for example, since you know that will be imported by Django on startup. The register function would take the app name and the url and build a dictionary which could then be passed to each template via a context processor.

Related

How to build URL from template to the admin pages

Once deployed onto a production server my django app URLs are like
http://example.com/myapp/Widget/10
I'd like to put a link to the admin forms in my pages. Not sure
the correct way to put an
Go to admin
in my templates
so that I can correctly get from a URL like the above for widgets to the admin page which would be http://example.com/myapp/admin
I can't do something like
<a href="/admin">
because this only works on the dev server but not in production. Is there a way to use the syntax like {% url admin %} to get to the admin page correctly? Not sure how to code the urlpattern if I add something like
url(r'^admin$', ?, name='admin')
in my urls.py

How to create an article list view in django-cms

I am an absolute beginner in django-cms, just acquired some pieces of knowledge to create templates. Just wondering, how to create a portal page that has a few acticles in each different categories?
Please simply point out a practical way to do, no real code is needed.
Thank you.
As others have pointed out, the way to do this is by hooking your CMS page to another set of views. Django-CMS provides application hooks:
#cms_app.py
from cms.app_base import CMSApp
from cms.apphook_pool import apphook_pool
class YourModuleApp(CMSApp):
name = 'My App MOdule'
urls = ['my_app.urls']
apphook_pool.register(YourModuleApp)
So, if you had a module called "my_app" with a urls.py in it, Django-CMS will add those patterns to the page. Look in the "Advanced Settings" section of the page in admin for the application drop-down menu.
Once the app is hooked to the page, Django-CMS will pull any content and the layout template from the information it holds, then hand off processing to the additional URL patterns that are hooked to it. That's how you can pull in another model, add a form, handle a POST, etc.
You could just do it the normal Django way. Create a normal Django app, with a URL pointing to a view that renders a template. The view could look like this:
from django.shortcuts import render
from cms.models import Page
def articles(request):
pages = Page.objects.public()
render(request, 'example.html', {'pages': pages})
And the template could look like this:
{% load cms_tags %}
{% for page in pages %}
<p>{% page_attribute "page_title" page %}</p>
{% endfor %}
You could stop here. Or you could have...
Additional Django CMS integration with AppHooks
Do you want your non-developer content managers to be able to put a list of articles wherever they want? This is where AppHooks come in.
Create a CMSApp class in the file appname/cms_app.py like this:
from cms.app_base import CMSApp
from cms.apphook_pool import apphook_pool
from django.utils.translation import ugettext_lazy as _
class ArticleListApp(CMSApp):
app_name = 'articlelist'
name = _('Article List')
def get_urls(self, page=None, language=None, **kwargs):
return ['articlelist.urls']
apphook_pool.register(YourModuleApp)
Delete the URL entry in your project-wide urls.py as you no longer need it. Your app's urls.py needs to include a view for the regex ^$.
Now you or any content manager user with necessary permissions can create a page in the admin interface, and modify the advanced settings to select the "Article List" application:
One gotcha is that this will have no effect until the page is published (as well as all of its ancestor pages).

overriding default templates of django-allauth

I used this social registration/signup library django allauth for a project of mine. How do i customize the default templates and forms to give a better look and feel?
Assuming you have set a project level templates directory using the TEMPLATE_DIRS setting like:
TEMPLATE_DIRS = [
os.path.join(PROJECT_DIR, 'templates'),
]
You should be able to copy all of the folders shown here into that directory and edit them as you need. Most of the templates seem to be filling a {% block content %} block, so it's probably easiest if your site_base.html template defines that block somewhere.
If you haven't set TEMPLATE_DIRS, you can do the same thing, but copy the template folders into the templates directory of one of your apps. I prefer to set TEMPLATE_DIRS and keep the main site templates like base.html there, since they don't really belong to a particular app, but that's really just a preference; the template loader should find them either way.
In your views:
from allauth.account.views import SignupView, LoginView
class MySignupView(SignupView):
template_name = 'my_signup.html'
class MyLoginView(LoginView):
template_name = 'my_login.html'
Do pay attention to the examples and docs for structuring your own templates.
Watch this piece in the example templates though:
<form id="signup_form" method="post" action="{% url 'account_signup' %}">
I had to remove the URL link to make it work properly in my own template:
<form id="signup_form" method="post" action="">'
the latest version of all-auth on github has its templates outside, however the one on Pypi is not, all you need to do is clone the repo in your project directory and override the templates. As simple as that.
Use the same logic as overriding admin templates.
Have a look at the example application; it has a templates folder that indicates the layout of the necessary templates
All of these are good suggestions.
Most of these ought to work.
The only thing that worked for me, though, was to include my own templates in "project/app/templates/account/" and make sure that "django-allauth" gets listed in INSTALLED_APPS after my own app.
I discovered this here.
To customize django-allauth after installing it, copy it from site-packages and paste it in your project apps directory. In this way the default allauth app and its templates being used will be those in your project's allauth app. Then if you want to modify signup.html of socialaccount then go to apps\allauth\templates\socialaccount\signup.html and modify it by editting inside 'block content' tag:
{% block content %}
// your customized html for signup form
{% endblock %}
Hope this will help you.

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: Reverse URL lookup with arbitrary URL components

Assume a Django app, shop, whose urls.py is included in the main urls.py:
# main urls.py
urlpatterns = patterns('',
(r'^[^/]+/shop/', include('shop.urls')),
)
Note, that there is an arbitrary prefix before /shop. This is of no interest for the shop app, it's only interesting to some middleware.
The shop.urls could look like this:
# shop's urls.py
urlpatterns = patterns('',
url(r'^$', index, name="shop_index"),
url(r'^product/(?P<id>[^/]+)$', product, name="shop_product"),
)
I now want to use {% url %} in my shop templates. What overthrows this plan is, that the generated URL doesn't include the prefix from the global urls.py. The generated URLs look more like, e.g.
/^/shop/product/1
Question: Now, is there a possibility (in Django 1.2 or svn trunk) to enable the reverse lookup to use just the same prefix, that is there in the current URL? For example, if request.path is /foo/shop/, that {% urls shop_product id=1 %} returns
/foo/shop/product/1
The only way I came up with is to embrace the prefix and send it to the shop app views, but this is a really bad solution, since shop then has to deal with something it doesn't need at all.
No there is not, not a straightforward django way anyway. Django has no idea of knowing what you mean with [^/]+, in your case that it is a prefix that should be dynamically be added to a reverse url lookup.
I wonder also why you add middleware specific info to an url and not even as GET paramters, doing something like this is jsut asking for trouble imho. If you say the prefix is middleware-specific, it doesn't make sense to mess with it somewhere outside of the middleware.
A solution that could work (did not test it) is setting in your middleware a request context variable like environment_prefix, then append this manually before the url. So something like this:
/{{ environment_prefix }}{% filter slice:"1:" %}{% url shopview %}{% endfilter %}
Another possiblity is to try to implemented your own url template tag (inheritted from the url templatetag) to always include the current prefix which can again be a context variable set by the middleware.