We have a notification system in place (model extract below) and every time the site needs to notify any user about anything, it creates a notification. Now we want to show that on every site (we use a global template) a counter of the unread messages without changing every view to deliver it to the template. Is there any way to do this right?
class Notification(models.Model):
n_id = models.AutoField(primary_key=True)
n_body = models.CharField(max_length=1000, null=True)
n_recipient = models.ForeignKey(User, related_name='Recipient', on_delete=models.CASCADE)
n_read_status = models.BooleanField(default=False)
Our query would be Notification.objects.filter(n_recipient=request.user, n_read_status=False).count() but we don't want to call it in every view manually.
You can use 2 options:
1 - Using template tags:
Custom template tags and filters
Basically call a custom template tag with user object and get the notification data when you need it.
2 - Using django middleware:
Django docs: Middleware
Add your notification data to request context. Example here:
Django - How to modify template context from middleware
Option one makes more sense cause you might not need notification data everywhere and it requires to check for user objects and ... which can be extra unnecessary process and error handling is kinda harder and if you don't handle it properly, it can cause errors on every page while template tags only used when needed.
Like #DanielRoseman and #Navid2zp pointed out, the correct solution would probably be a template tag which is created by adding a templatetags folder (more info in Django docs). Our working code below:
HTML template
{% load notifications %}
{{ messages|getmessages:request.user }}
notifications.py
from django import template
from Toolbox.models import Notification
from django.contrib.auth.models import User
register = template.Library()
#register.filter
def getmessages(value, user):
number = Notification.objects.filter(n_recipient=user, n_read_status=False).count()
return '' if number == 0 else ' (' + str(number) + ')'
Related
In my Django project I have a database that is populated from outside Django, but needs the Django functionality to display data in the database in a user friendly manner. The legacy database structure is as follows:
class SomeModel(models.Model):
#some fields
group_access = models.ForeignKey(AccessGroup,on_delete=models.CASCADE()
class AccessGroup(models.Model):
group_name = models.CharField(max_length=200)
The users have a custom User profile with manytomany relationship with group names assigned to the specific user:
class CustomUser(models.Model):
#some values
projects = models.ManyToManyField(AccessGroup)
Currently I am able to display data from all groups a user has access to, but what I am looking for is a way to create a drop down menu so that users can switch between groups without the need to log out or reenter group on every view.
You could try something like this:
AccessGroup.objects.filter(CustomUser__pk=1)
Or
CustomUser.objects.filter(AccessGroup__group_name='GropName')
https://docs.djangoproject.com/en/2.0/topics/db/examples/many_to_many/
you can extend the django user model, somthing like
from django.contrib.auth.models import User
class CustomUser(models.Model):
projects = models.ManyToManyField(AccessGroup)
class UserProfile(models.Model):
user = models.OneToOneField(User, unique=True)
custom_user = models.ForeignKey(CustomUser, unique=False)
class SomeModel(models.Model):
#some fields
group_access = models.ForeignKey(AccessGroup,on_delete=models.CASCADE()
class AccessGroup(models.Model):
group_name = models.CharField(max_length=200)
then something like this to get the data in your view
def index(request):
AccessGroup.objects.filter(user__id=persion(request.user).id)
I'll assume you know how to get the list of groups, and are just looking as to how to get this list into templates. If not, let me know and I'll explain that as well.
If you're trying to get a global variable into templates, there are really 3 main options:
Make a custom template tag that takes the current user as input, and generates this list as output.
Use Middleware to generate the list, and append it to the current context for each request
Use a method on your user class, or a mixin of it (really easy if you use a custom user class), and just call that method as user.method in your templates. Remember to exclude parentheses from the method call (only in templates), and keep in mind that this method shouldn't accept any parameters other than self.
Thank you everybody for getting me on the right track. What I ended up doing is writing a context processor for checking the user permissions:
#context_processors.py
def check_groups(request):
group_check = AccessGroup.objects.values('id','group_name').filter(projects=request.user.id)
return {
'group_check': group_check,
}
Afterwards I created a Bootstrap-select dropdown in my base.html
<select class="selecpicker">
<optgroup>
<option data-hidden="true">Choose group</option>
{% for grpup in group_check %}
<option val="group.id">{{ group.group_name }}</option>
{% endfor %}
</optgroup>
And the it is just a matter of users using it as means to switch access groups in views and passing the value via ajax to any other template views I come across.
Not the 100% what I was looking for, but it works and my users are happy.
I have this model Note:
class Note(models.Model):
category = models.ForeignKey(Category)
author = models.ForeignKey('auth.User')
title = models.CharField(max_length=40)
text = models.TextField()
created = models.DateTimeField(auto_now_add=True)
updated = models.DateTimeField(auto_now=True)
And I want to display this form:
class NoteEditForm(forms.ModelForm):
class Meta:
model = Note
fields = ('title', 'text')
in a template, but I want it to appear for each existing Note object in the database (it has to be that way). I've done something like that but then I hardcoded the form, pointing to the edit view URL with each object pk as a parameter; but I'm sure it has to be a clearer way, just I haven't found it. Could you guys help me with that? Thanks!
The easiest way to do this is to use a formset (also see model formsets).
For your Note model and NoteEditForm you could do something like this. You'd usually put this wherever you've defined your NoteEditForm but it can go in another file, such as views.py.
from django.forms import modelformset_factory
NoteEditFormSet = modelformset_factory(Note, form=NoteEditForm)
Using NoteEditFormSet in a view and template is almost the same as using a regular form, but there are a few differences to be aware of if you want to do anything complicated so have a look at the docs (view and template). If that's not clear enough, add a few details of what you're trying to do in your view and template and I'll try to help.
By default the formset will use Note.objects.all() as its queryset, which is what you say you want, but you can change that (details are covered in the docs).
Update:
To save an individual Note with an AJAX request I would add a second view to handle those requests. So if your formset for all Notes is served by a view at /notes/, you could add a view to handle your AJAX request at /notes/<note_id>/ (obviously just an example, adjust to fit your URL structure).
Then your JS on the /notes/ page is responsible for serializing the data for a single note and making the request to /notes/<note_id>/ (remember the CSRF token).
The HTML inputs generated for the formset have their IDs prefixed with something like id_form-<number>- and there are hidden inputs containing Note primary keys which will let you work out which ID prefix applies to each note.
I would think about doing it like this
{% for note in Notequeryset %}
<form action={% url 'url_name_to_form' pk={{note.pk}} %}>
{{form.as_p}}
</form>
{% endfor %}
Let me know what you think
I'm building a Django site and in various places I want to display a little question mark and if the user hovers over it, it should display a message. I think the best approach is to store the tooltip in the database like this:
class Tooltip(models.Model):
key = models.Charfield(max_length=20)
message = models.Charfield(max_length=300)
My question is what is the best way to get the tooltips in the templates? So far my idea is to create a custom template tag, which will do the lookup and return the result, but I haven't used template tags much and wondering if there is a problem with this approach?
{% tooltip 'message_key' %}
The Django documentation explains how to create template tags.
Could be as simple as:
from django import template
register = template.Library()
#register.simple_tag
def tooltip(key):
try:
tooltip = Tooltip.objects.get(key=key)
return tooltip.message
except Tooltip.DoesNotExist:
return '' # or return a message stating that the key does not exist.
You need to save this file in your app's templatetags/ directory, along with an __init__.py. You can then {% load "<appname>" %} in your template and use the tag like you suggested.
While you might not need a database to store these, if you do, at least add an index to the key field on your model.
In my application I have set up a main template, all other templates are an extension for it. The main template only includes static data.
Objective: I want an alert - small crimson bar at the top - to be displayed at all pages served by the app.
Current solution is to just write it in raw HTML in the main template.
My views are all set up as follows:
urls.py:
url(r'^exam/(?P<exam_id>[0-9]+)/$', views.exam, name='exam'),
url(r'^person/(?P<person_id>[0-9]+)/$', views.person, name='person'),
...
views.py:
def exam(request, exam_id):
exam = get_object_or_404(Exam, pk=exam_id)
return render(request, 'exam.html', {'exam': exam})
def person(request, person_id):
...
I.e. all are quite primitive and main_template itself is not mentioned anywhere except for in the templates themselves.
To make alert dynamic and configurable from django-admin, I am planning to:
Create a model for it:
Makes sense, since the alert might have some properties beyond base 'message content'
Somehow design and write a view that would populate the main template with dynamic content.
Question:
How do I refactor the views to reach the objective - make the main template dynamic - while breaking current views as little as possible?
My suggestions would be to create a model for storing your alert. The create an inclusion tag (check here). In this way you can add this tag to whichever template you want (possibly base.html create an alert block and use this tag inside that block). This will ensure that with minimal refactor of your code you'll get the feature you want following the best practice. Hope this helps .. :)
I assume that you want to have a small alert on your exams page.(exams.html)
Create a simple model,
class Alert(models.Model):
message = models.CharField(max_length=255, blank=False, null=False)
date_added = models.DateTimeField(auto_now_add=True)
class Meta:
get_latest_by = "date_added"
In your current views.py,
def exam(request, exam_id):
exam = get_object_or_404(Exam, pk=exam_id)
alert = Alert.objects.latest() # Get latest alert message
# Pass alert object along with other variables.
return render(request, 'exam.html', {'exam': exam, 'alert': alert})
In your exams.html template
<div class='alert'>
<p>{{ alert.message }}</p>
</div>
Hope this helps.
I have a form looking like this:
class MarketingActionForm(forms.ModelForm):
contact = ManyToManyByLetter(Contact, field_name="first_name")
#contact = AjaxManyToManyField(Contact, DICT_LOOKUP)
class Meta:
model = MarketingAction
exclude = ('created_by',)
class Media:
js = (
settings.ADMIN_MEDIA_PREFIX + "js/SelectBox.js",
settings.ADMIN_MEDIA_PREFIX + "js/SelectFilter2.js",
settings.MEDIA_URL + "js/jquery.js",
settings.MEDIA_URL + "js/ajax_filtered_fields.js",
)
I process this form with a view to the template. Now I`m wondering why the Media class is not automatically processed in the template, at least it does not show up in the .html output.
Therefore i want to ask what i have to do in order that the media definitions will show up in the .html output.
I did not find it in the django .docs. Therefore i thought it will be processed automatically.
You'll need to add {{form.media}} in the template yourself. References to form media are not inserted automatically.
It would be very hard to do since entire html document including the <head> section is to be typed by the template designer and django would have to guess where to insert the links if it were to attempt to do it automatically (it would be especially hard to guess correctly for the javascript media - if there are dependencies between scripts)
I believe the Media class is used in the admin, by classes which subclass admin.ModelAdmin
http://docs.djangoproject.com/en/dev/ref/contrib/admin/#modeladmin-media-definitions