django choose template from admin panel without restarting server - django

I'm trying to create a website with multiple template for all pages.
I've created a templates folder and there are 3 folders in it. each folder contains base.html, home.html, etc.
Admin can choose each template from admin panel and in my view load template like this.
class HomeView(TemplateView):
default_template = CustomTemplate.objects.first().name
template_name = default_template + '/home.html'
The problem is I have to restart server to apply admin's changes.
Is there any way to do this without restarting server?
I've also tried to enable / disable loader cache but I guess the problem is not depends to cache system.

Anything defined directly at class level will persist for the entirety of a process.
Luckily, Django's class-based views provide a series of hooks so that you can define things on a per-request basis. In this case, the method you want is get_template_names (which returns a list of templates to search for).
So:
class HomeView(TemplateView):
def get_template_names(self):
default_template = CustomTemplate.objects.first().name
return ['{}/home.html'.format(default_template)]

Related

Add own functions in views.py wagtail

I have setup a wagtail website. It works great for postings like a blog and simply add new pages.
But what if I want to add some extra functions to a page. Like showing values from my own database in a table.
Normally i use a models.py, views.py and template.py. But now I don’t see any views.py to add functions or a urls.py to redirect to an url?
Don’t know where to start!
Or is this not the meaning of a wagtail site, to customize it that way?
Thnx in advanced.
You can certainly add additional data to pages. One option is to add the additional information to the context of a page type by overriding its get_context method. For example, this page is just a place to display a bunch of links. The links and the collections they belong to are plain old Django models (managed as snippets). And then there is a page model that queries the database like this:
def get_context(self, request, *args, **kwargs):
context = super().get_context(request, *args, **kwargs)
collection_tuples = []
site = Site.find_for_request(request)
for collection in Collection.objects.filter(links__audiences=self.audience, site=site).distinct():
links = Link.objects.filter(audiences=self.audience, collections=collection, site=site)
collection_tuples.append((collection.name, links.order_by('text')))
# sort collection tuples by the collection name before sending to the template
context['collection_tuples'] = sorted(collection_tuples, key=lambda x: x[0], reverse=False)
return context
Another option is to do basically the same thing - but in a StructBlock. Then you can include the StructBlock in a StreamField on your page. Most of the Caltech site is written using blocks that can be included in one large StreamField on a page. Some of those blocks manage their own content, e.g. rich text blocks or image blocks, but others query data and render it in a block template.
To add to #cnk's excellent answer - you can absolutely use views.py and urls.py just as you would in an ordinary Django project. However, any views you define in that way will be available at a fixed URL, which means they'll be distinct from the Wagtail page system (where the URL for a page is determined by the page slug that the editor chooses within the Wagtail admin).
If you're defining URLs this way, make sure they appear above the include(wagtail_urls) route in your project's top-level urls.py.

Create multiple website templates with django cms or any tool

Can Django be used to create a website for creating custom websites depending on the user needs? There will be more than one template and each user will have a custom url for their websites.
I don't know if this can this be achieved with Django CMS.
Sure, it is possible but it requires ton of work, because you need to write bunch of templates, or/and logic that handles templates.
You could write TemplateMixin, that depending on passed domain, or kwarg in url changes the template in the View. So, for example, it could look like that:
class TemplateMixin:
template_name = None
request = None
model = None
def get_template_names(self):
"""
Return a list of template names to be used for the request.
Must return a list. May not be called if render_to_response() is overridden.
"""
db_settings = Settings.objects.get_or_create()[0]
if self.template_name is None:
raise ImproperlyConfigured(
'TemplateResponseMixin requires either a definition of '
'\"template_name\" or an implementation of \"get_template_names()\"')
template = db_settings.website_template
ready_template = '/'.join([template, self.template_name])
return [ready_template]
There are also solutions for handling multiple domains, so you could detect what current domain is via self.request in get_template_names method. The templates are handled by prefix. The kwargs also should be in self.kwargs, so you can detect which site you need to present via custom namespace url.
You can also create WebsiteSettings model that handles various templates, and is manageable via simple custom dashboard panel, and belongs to user, or user groups. The fields that it should have is 'template' with choices attribute, relation to User (or CustomUser model), and bunch of settings you want to give a user (or you could have different model('s) for that).
The solution I bring here is simple to show the idea - but it can be insufficient, to accomplish what you really want you must write the whole mixins, views etc. logic yourself.

Django: Custom default Manager with user based queries for all views/interfaces (custom, admin, graphene, rest, ...)

In Django I'm trying to implement some kind of a "security middleware", which gives access to certain db information only, if the logged in user matches.
Up to now I found two approaches: middleware or custom function in manager (Both explained here Django custom managers - how do I return only objects created by the logged-in user?).
Example for cleanest solution: custom function
class UserContactManager(models.Manager):
def for_user(self, user):
return self.get_query_set().filter(creator=user)
class MyUser(models.Model):
# Managers
objects = UserContactManager()
# then use it like this in views
data = MyUser.objects.for_user(request.user)
However, this solution only works, if you have control over the code which invokes this custom function (here: for_user()).
But if you are using third parties apps like Django-REST, Graphene or AdminViews, they don't have the possibility to configure a specific query-func to use.
My goal:
Replace the default Manager
Add user-based filters to query_set
Use the model as normal in all app configurations
Idea (pseudo code!)
from django.x import current_user
class UserBasedManager(models.Manager):
def get_queryset(self):
return super().get_queryset().filter(author=current_user.name)
class Book(models.Model):
title = models.CharField(max_length=100)
author = models.CharField(max_length=50)
objects = UserBasedManager() # The default manager.
Now I could use the model Book as normal in all extensions.
I know this solution would have some drawbacks, which are okay for me:
Can be only used when user is available (e.g. no direct script support)
Handling no or anonymous user is missing (left it away to keep example short)
My use case
I have a project, which shall give access to data by different interfaces (admin pages, custom views, rest, graphql).
As I have so many interfaces, I don't want to implement the access-rights in the views. This would cost too much time and is hard to maintain and the risk for security problems in one specific view/interface is too high.
Let's say I'm storing commits of git repositories in a database.
And the user shall get access to all commits, which are part of repos the user has READ access.
The question is: How can I implement this as a generic, view/interface independent solution?
Install django-threadlocals package and call get_current_user function to get the current user
from threadlocals.threadlocals import get_current_user
class UserBasedManager(models.Manager):
def get_queryset(self):
current_user = get_current_user()
return super().get_queryset().filter(author=current_user.name)

Django : proper way to make website title editable in admin

I'm building a website with Django. I have a main title for the website in my header template, currently hard-coded in it.
How can I make it editable in the admin by the website administrator (or any user with the right credentials) ? Ideally I'd also like to be able to make more such site-wide (or app-wide) attributes editable in the admin (such as site - or app- description, for instance).
I have in mind something like WordPress' bloginfo() . A practical example of this is washingtonpost.com who changed their moto in their header to "Democracy dies in darkness" a few weeks ago.
Of course once the title (or any other attribute) has been edited in the admin I need to be able to get it from within my template.
You can create a simple model to store dynamic website parameters like this for example:
class WebsiteParam(models.Model):
key = models.CharField(max_length=50)
val = models.CharField(max_length=1024)
Then define custom template context processor settings_processor.py
def settings_processor(request):
vals = {x.key: v.val for x in WebsiteParam.objects.all()}
return {'website_settings': vals}
Add this processor into your django settings.py something like:
from django.conf import global_settings
TEMPLATE_CONTEXT_PROCESSORS = global_settings.TEMPLATE_CONTEXT_PROCESSORS + (
"myapp.settings_processor.settings_processor",
)
And you will be able to use your settings in all of your templates like
<html><head><title>{{website_settings.title}}</title>...
if you have settings with title key added into the database
You should of course add caching into request context processor and other conditions if necessary

One admin for multiple sites

I have two sites with different SITE_IDs, but I want to have only one admin interface for both sites.
I have a model, which is just an extended FlatPage:
# models.py
class SFlatPage(FlatPage):
currobjects = CurrentSiteManager('sites')
galleries = models.ManyToManyField(Gallery)
# etc
# admin.py
class SFlatPageAdmin(FlatPageAdmin):
fieldsets = None
admin.site.register(SFlatPage, SFlatPageAdmin)
admin.site.unregister(FlatPage)
I don't know why, but there are only pages for current site in admin interface. On http://site1.com/admin/ I see flatpages for site1, on http://site2.com/admin/ I see flatpages for site2. But I want to see all pages in http://site1.com/admin/ interface! What am I doing wrong?
It's because of CurrentSiteManager. According to the documentation "it's a model manager that automatically filters its queries to include only objects associated with the current Site."
Remove the line and everyting should work as expected. Or if you make use of currobjects somewhere else in your code note that admin interface always use the first manager specified, so you need to specify the standard manager first, like this:
# models.py
class SFlatPage(FlatPage):
objects = models.Manager() # Is first, so will be used by admin
currobjects = CurrentSiteManager('sites') # your alternative manager
galleries = models.ManyToManyField(Gallery)