Django cache invalidation using versioning - python

I have a template fragment caching like:
{% cache 3600 my_list request.path %}
... some html
{% endcache %}
the request.path could be in the following form:
list/2012-01-01
list/2012-02-01
...
I've searched so far and read that to invalidate everything under the name 'my_list' I could use versioning and just increment the version using cache.incr_version, but it accepts a key which I don't know since there's no way to predict what URL the user is accessing.
Basically those urls just list some models and when a new one is added, I want to invalidate the cache so it reflects the newly added record. But I can't call .delete or .incr_version because I don't know the full key.

Related

Django : how to use modified queryset in templates without saving modification

In my views.py, I do:
context_data = {}
all_answers = Answer.objects.all()
for i in range(0, len(all_answers)) :
all_answers[i].text = modify_text(all_answers[i].text, request)
context_data ["all_answers"] = all_answers
print(context_data ["all_answers"][0].text) #Check that it was modified
return render(request, 'template.html', context_data)
And in my templates I have like :
{% for answer in all_answers.all %}
{{ answer.text}}
{% endfor %}
The check shows that the modification is made, yet it's my template the answer.text is the unmodified data from the database.
I saw that the type of the context_data ["all_answers"] is a queryset, that I guess is triggered during the template rendering, making any unsaved changes useless, how to make my changes show in the template?
I have tried :
context_data ["all_answers"] = list(all_answers)
To load the queryset. The check works but then nothing shows in the template (1)
An error arises during the template render when I used this function to load the queryset into a list of dict.
I also saw a [linked question without answer].3
I don't want to save the changes as they are customized for each request (for each user basically).
** TLDR: How to see my modifications in the templates without saving the changes into the database ?**
PS: Using Python 3.6.8, Django 2.2.3.
Querysets are not data holders they are just lazy references, and get evaluated on runtime. The problem with your code is that you are modifying instances in queryset (which is also wrong way you should iterate using in operator like).
for answer in all_answers:
answer.text = modify_text(answer.text, request)
The real problem is that you are calling all method on queryset in your template again all_answers.all which returns a fresh queryset and without changes you made so you should, in template, do
{% for answer in all_answers %}
{{ answer.text}}
{% endfor %}
In Python you should never iterate through range(len(something)), but always over the thing itself. In the case of Django querysets this is even more important, since accessing an item in an unevaluated queryset via its index ([i]) actually causes a separate request to the database each time.
Do this instead:
for answer in all_answers:
answer.text = modify_text(answer.text, request)
context_data ["all_answers"] = all_answers
print(context_data ["all_answers"][0].text) #Check that it was modified
Note that the loop evaluates the queryset, so the [0] there doesn't cause another db request.

how to use dynamic templates in python pyramid

i've finished developing a website, it's working fine however i am trying to optimize my website by adding dynamic templates, and want to make sure that if it can be done on pyramid python.
for example, in my jinja template i have the following:
{% block article_detail %}
<form action="{{request.route_url('Sports_News_Action',action=action)}}" method="post" class="form">
{% if action =='edit' %}
{{ form.id() }}
example in my controller:
#view_config(route_name='Sports_News_Action', match_param='action=create',
renderer='StarAdmin:templates/edit_sports.jinja2')
def general_create(request):
entry = SportNews()
the request route will have to match the one in my controller in order to run the function. what i want to do is how do i replace the one in jinja with a dynamic variable, to use the one jinja template lets say for different views/controllers with different route_names.
I think in your situation the simplest solution is to leave action undefined and the browser will submit the request to the current url. You only need to specify action if you want to submit the form to a different url than the current. That being said, you can use lots of different options in pyramid to generate a url as well. For example, request.url is the current url, or request.matched_route.name is the name of the current matched route.

Django messages repeating itself instead of updating

classinfo = EventType.objects.all()
length = EventType.objects.all().count()
for i in range(length):
messages.success(request, classinfo[i])
So I'm using this to print out a list of all events that are located in EventType.
Image
This is what it looks like. However, when I add another item to the list using the Class Add feature, it repeats the original list again and then adds the additional event. But if I add an additional another event after this it will add it to the list properly. How can I go about fixing this so it doesn't repeat the initial list when I add an event?
How it looks right now when I add an event
This is what the HTML looks like:
{% for message in messages %}
<li>
{{ message }}
</li>
{% endfor %}
By default Django uses the FallbackStorage class, which means that it will first use CookieStorage to store your messages. As long as your cookie does not exceed 2KB, then it will store all your messages in a cookie. What you are doing is creating messages when you run your request, all these messages are stored in a cookie. You then add an event and make another request. The view will then loop through all your EventType's again and add it to the cookie, along with the original content of the cookie from the first request. I don't know why when you add an EventType a third time it shows the list properly, perhaps you are at the size limit of the cookie and it drops its existing contents.
Do you really need to store messages? Messages are used as a logging mechanism. You are using messages to iterate over a model, which is unnecessary. Simply pass classinfo into your context dictionary and iterate over it instead of messages:
view.py:
classinfo = EventType.objects.all()
template.html:
{% for event in classinfo %}
<li>{{ event }}</li>
{% endfor %}
Better yet, simply use Django's ListView, this is exactly what it is for:
class EventListView(ListView):
model = EventType

Automatically add a variable into context on per-application basis in Django?

I want to add a context variable in Django, so that I could define its value on per-application basis, or leave it empty.
Example:
apps/someapp/views.py:
def_context_var('app_name', 'Calendar')
templates/base.html:
{% if app_name %}You are in {{ app_name }} app.{% endif %}
....
{% if app_name %}Subsections of {{ app_name }}: ...{% endif %}
I considered the following:
Declare a variable in the app (in a view, or in URLs), and make a context processor. But I can't understang how to extract that var given the request object.
Put decorators on views. Hm, I don't like the idea: too much boilerplate or duplicated code.
#1 but nicer: make methods (like in the example above) that are executed on server restart, write the data into a dict, then a context processor somehow (how?) gets the application name and extracts the data from the dict. Where do I put the method, the dict, how does the context processor know where the view object is in?
You can call resolve(request.path) in a context processor to resolve the current url. See the django documentation on resolve for its return values, especially app_name.

Django: Template context processor request variable

I am trying to implement django-facebookconnect, for I need to check if a user logged in via Facebook or a regular user.
At the template, I can check if user logged in via facebook by checking request.facebook.uid
such as:
{% if is_facebook %}
{% show_facebook_photo user %}
{% endif %}
For this, I need to pass is_facebook': request.facebook.uid to the template and I will be using this in everywhere thus I want tried to apply it to an existing template context processor and call the snipplet above at the base.html, and it works fine for Foo objects:
def global_variables(request):
from django.conf import settings
from myproject.myapp.models import Foo
return {'is_facebook': request.facebook.uid,'foo_list': Foo.objects.all()}
I can list Foo objects at any view without any issue however it fails for this new is_facebook, it simply returns nothing.
If I pass 'is_facebook': request.facebook.uid in every single view , it works but I need this globally for any view rendering.
If you have access via the request object, why do you need to add a special is_facebook boolean at all? Just enable the built-in django.core.context_processors.request and this will ensure that request is present in all templates, then you can do this:
{% if request.facebook.uid %}
It could be a timing issue. Make sure that the Common middleware comes before the facebook middleware in your settings file. You can probably debug and see when the facebook middleware is modifying the request and when your context processor is invoked. That may give you some clue as to why this is happening. But, as Daniel said, you can always just use the request object in your templates.

Categories