Site menu using django - python

I want a menu on my web site.
I want this menu to be editable via django admin. So, I need to create a model for it.
Once I've created model for my menu, I can use it in my views.py:
def main(request):
menu_items = MenuItem.objects.all()
return direct_to_template(request, 'main.html', {'menu_items': menu_items})
It is ok. But... wait a minute...
If I have several pages, it will look like:
def main(request):
menu_items = MenuItem.objects.all()
return direct_to_template(request, 'main.html', {'menu_items': menu_items})
def page1(request):
menu_items = MenuItem.objects.all()
return direct_to_template(request, 'page1.html', {'menu_items': menu_items})
def page2(request):
menu_items = MenuItem.objects.all()
return direct_to_template(request, 'page2.html', {'menu_items': menu_items})`
Is there any way not to write the same code everytime I create new function?

Yep, create a context processor and your menu will be available on every page
Inside one of your apps create a file menu_context_processor.py (or similar)
from myapp.models import MenuItem
def menu(request):
return {'menu': MenuItem.objects.all() }
and add it to your TEMPLATE_CONTEXT_PROCESSOR setting in settings.py
TEMPLATE_CONTEXT_PROCESSORS = (
...,
'myapp.menu_context_processor.menu'
)
and now, in every template, you will have access to {{ menu }}. These are used to make {{ STATIC_URL }} and `{{ MEDIA_URL }}' available to you throughout your application and are useful for variables you want to include everywhere.

Ok, Timmy suggested you a Django way and it works great. By the way, I know a pythonic way that you can use decorators like this:
def apply_menu(func):
def wrapper(*args, **kwargs):
request = args[0]
menu_items = MenuItem.objects.all()
vars = {'menu_items': menu_items}
if kwargs:
page, template_var = func(*args, **kwargs)
else: page, template_var = func(*args)
vars.update( template_var)
return return direct_to_template(request, page, vars)
return wrapper
And then use the decorator this way:
#apply_menu
def main(request):
your_extra_vars = {}
return 'main.html', your_extra_vars

Related

Setting up conditional views in Django based on user permissions

I'm attempting to set up an index page in django that serves different content based on the user permissions (2+ types of users). I've looked into using the #permission_required decorator but it seems a bit wasteful and repetitive to use that with a view that's been mostly repeated. There's also no good fallback method that I can see (I can't do a #permission_required(!'jobs.can_edit')).
views.py:
#permission_required('jobs.can_add')
def index(request):
jobs = Job.objects.all
context = {
"jobs": jobs,
}
return render(request, 'jobs/index.html', context)
#permission_required('jobs.can_edit')
def index(request):
jobs = some.different.data
context = {
"jobs": jobs,
}
return render(request, 'jobs/index.html', context)
Is there an easier way to hook this into the index function and change the context based on user permissions? My ideal scenario would be more like this
imaginary views.py:
def index(request):
if user.can_add:
context = x
return render(request, 'jobs/index/can-add.html', context)
context = y
return render(request, 'jobs/index/can-edit.html', context)
I've also set the three user groups up by name, but I don't see much documentation on accessing group names.
If you are using django permission then you can do this in view
def index(request):
if request.user.has_perm('app_name.permission_name'):
#return something if true
#return something in else case
I recommend using groups and assigning permission to a group and check if a user belongs to a group in the view
try this.
I was inspired with that and I want to share my response too for others but in my case I use DRF and django group.
class ShopOrderCreateList(APIView):
permission_classes = [AllowAny]
#staticmethod
def get_object(shop_id):
try:
return Shop.objects.get(id=shop_id)
except Shop.DoesNotExist:
raise Http404
def get(self, request, shop_id=None):
shop = self.get_object(shop_id)
orders = Order.objects.filter(shop__id=shop.id)
# orders = manager.order_set.all()
# shop_orders = shop
# order_numbers = orders.count()
serializer = OrderSerializer(orders, many=True)
return Response(serializer.data)
#staticmethod
def post(request, shop_id=None):
if request.user.groups.filter(name='admin').exists():
manager = request.user.manager
elif request.user.groups.filter(name='master').exists():
master = request.user.master
manager = master.manager

Kwargs isnt passing

I am trying to pass some information to a view in Django in order to filter a list and then pass this back to the user. In order to do so I have the following in my urls.py:
url(r'^info/user/(?P<user_id>\d+)$', views.UserInfoView, name='active_reservations'),
and the following view defined:
def UserInfoView(request, **kwargs):
template = "parking/detail_user.html"
user = User.objects.filter(user=self.kwargs['user_id'])
context = {"user": user}
return render(request, template, context)
However, each time I try this I get the error: NameError at /info/user/1
global name 'self' is not defined
Any ideas?
You should change the view function. Replace **kwargs with user_id
def UserInfoView(request, user_id):
template = "parking/detail_user.html"
user = User.objects.filter(user=user_id)
context = {"user": user}
return render(request, template, context)
kwargs is not an attribute of self. Your code should be:
user = User.objects.filter(user=kwargs['user_id'])

Django: Extend context of class based view with Admin context

I have a class based view which just displays a list of configurations.
This view is added to the Django Admin site by using the following code:
#admin.register(ZbxHostConf)
class ZbxHostConfListViewAdmin(admin.ModelAdmin):
review_template = 'admin/admzbxhostconf_list.html'
def get_urls(self):
urls = super(ZbxHostConfListViewAdmin, self).get_urls()
my_urls = patterns('',
(r'^zbxhostconflist/$', self.admin_site.admin_view(self.review)),
)
return my_urls + urls
def review(self, request):
return ZbxHostConfListView.as_view()(request)
The template extends the admin/base_site.html template. I can access the site only after I log in to the Django Admin site. Unfortunately the template cannot access the context data provided by the admin view.
As the Django documentation suggests the context data will be provided directly to the TemplateResponse function:
def my_view(self, request):
# ...
context = dict(
# Include common variables for rendering the admin template.
self.admin_site.each_context(request),
# Anything else you want in the context...
key=value,
)
return TemplateResponse(request, "sometemplate.html", context)
For function-based views there is the possibility of the extra_context argument but class-based views don't provide this argument. I guess I have to modify the get_context_data function but I don't really understand how I can provide the admin context data to the get_context_data function of my class based view. Any suggestions?
This might not be a correct answer, but I believed you could try something like this.
#!/usr/bin/python3
from django.contrib import admin
class MyTemplateView(TemplateView):
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context.update(admin.site.each_context(self.request))
return context

Django admin redirect in SimpleListFilter

Is it possible to create one redirect in SimpleListFilter.
I try user django admin filter and redirect to url.
Is there a way to do so?
class ListFilter(SimpleListFilter):
title = "test"
def lookup(self, request, model_admin):
return (
('1t', 'First test'),
)
def queryset(self, request, queryset):
if self.value() == '1t':
redirect('/admin/test/3test/')
class TestAdmin(admin.ModelAdmin):
list_filter(ListFilter,)
Short answer: No. The queryset method should return a filtered queryset, not a http response.
If you really want to add a link in the filter sidebar, you can hack it like this:
class ListFilter(SimpleListFilter):
title = "test"
template = 'admin_sidebar_links.html'
def lookups(self, request, model_admin):
return (,)
def queryset(self, request, queryset):
return queryset
Then create an html file in your templates directory called 'admin_sidebar_links.html', containing something along these lines:
<h3>Filter Title</h3>
<ul>
<li>Link</li>
</ul>

Django Class Views and Reverse Urls

I have a good many class based views that use reverse(name, args) to find urls and pass this to templates. However, the problem is class based views must be instantiated before urlpatterns can be defined. This means the class is instantiated while urlpatterns is empty leading to reverse throwing errors. I've been working around this by passing lambda: reverse(name, args) to my templates but surely there is a better solution.
As a simple example the following fails with exception:
ImproperlyConfigured at xxxx
The included urlconf mysite.urls doesn't have any patterns in it
mysite.urls
from mysite.views import MyClassView
urlpatterns = patterns('',
url(r'^$' MyClassView.as_view(), name='home')
)
views.py
class MyClassView(View):
def get(self, request):
home_url = reverse('home')
return render(request, 'home.html', {'home_url':home_url})
home.html
<p><a href={{ home_url }}>Home</a></p>
I'm currently working around the problem by forcing reverse to run on template rendering by changing views.py to
class MyClassView(View):
def get(self, request):
home_url = lambda: reverse('home')
return render(request, 'home.html', {'home_url':home_url})
and it works, but this is really ugly and surely there is a better way. So is there a way to use reverse in class based views but avoid the cyclic dependency of urlpatterns requiring view requiring reverse requiring urlpatterns...
EDIT:
I'm using this as so:
views.py
def sidebar_activeusers(cls):
sidebar_dict = {'items' = []}
qs = models.random.filter.on.users
for user in qs:
item = {
'value': user.name,
'href': reverse('user_profile', args=[hash_id(user.id)])}
sidebar = loader.get_template('sidebar.html')
cls.base_dict['sidebar_html'] = sidebar.render(Context(sidebar_dict))
return cls
#sidebar_activeusers
class MyView1(View):
base_dict = {}
...
#other_sidebar_that_uses_same_sidebar_template
class MyView2(View):
basically I'd like to use the same sidebar template for a few different content types. Although the models displayed in the sidebar will be arbitrary the format will always be the same.
For your example MyClassView, it's ok to use reverse
class MyClassView(View):
def get(self, request):
home_url = reverse_lazy('home')
return render(request, 'home.html', {'home_url': home_url},
The reverse method is not called when the class is defined -- It is only called when the request is processed and the get method is called, so there shouldn't be an error. I have tested the example above and it works fine.
However, When you use your sidebar_activeusers decorator, the reverse call occurs before the url conf is loaded. In cases like these, you can use reverse_lazy. Here's an example of reverse_lazy in action:
from django.core.urlresolvers import reverse_lazy
class MyOtherClassView(View):
home_url = reverse_lazy('home')
def get(self, request):
return render(request, 'home.html', {'home_url':self.home_url})
This time, home_url is set when the class is defined, so reverse_lazy is required instead of reverse, because the url conf hasn't loaded yet.
Your home url can be accessed directly in your template by using declaring {% url 'home' %}. It's a standard way to access your named urls in your urls.py file.
You do not have to send the home_url variable to your template explicitly in your class-based view function.
In other words, in your home.html file:-
<p>Home</p>
will do.

Categories

Resources