Django Template session not updating values - python

I am developing a human vs human chess app in django. The part for pawn promotion is not working. The session values are changing,but not getting updated to django template.
The promote view
def promote(request):
#Update board with promoted piece
board = request.session['board']
target = request.session['promote_target']
board[target[0]][target[1]] = request.POST['piece']
request.session['board'] = board
request.session['promotion'] = False
request.session['player'] = 1
return render(request,'chess_app/Default.htm')
The js Function to call server
function promotion(piece){
//Function to promote pawns
//Add a confirm message
$.ajax({url:"{%url 'promote'%}",data:{'piece':piece},type:'post',success:function(){location.reload()}});
}
Everything works fine, but the session is not getting updated
It would be great if you can help.

Check this question I guess it should resolve your problem.
However IMHO use session in template is not very good solution, check this alternative options:
Middleware
You can get values from session and set it in request object in middleware. This option is sensible if you planning to use this values in different views and different templates.
View context
You can put values in view context. This option will be good if you planning to use values only in one view. (And of course you can create a mixin to share this functionality between different view
Inclusion tags
If you can extract part of template that use this data, you can create custom tag for this template and get all required data from request.
Context processor
If you need to share this values between all templates you may use context processors.

I'm not sure why this is not posted in this thread after being asked like a year ago.
The session values are changing, but not getting updated to django template.
To fix this you simply tell Django that sessions have been modified and then it knows to update them in the template:
# make Django update sessions in templates
request.session.modified = True
Here are the docs:
https://docs.djangoproject.com/en/2.2/topics/http/sessions/#when-sessions-are-saved
So to put this in context:
def promote(request):
#Update board with promoted piece
board = request.session['board']
target = request.session['promote_target']
board[target[0]][target[1]] = request.POST['piece']
request.session['board'] = board
request.session['promotion'] = False
request.session['player'] = 1
# make Django update sessions in templates
request.session.modified = True
return render(request,'chess_app/Default.htm') # you are good

Related

Django ChoiceField make only one choice readonly

I have a dropdown multichoice field in django, and I want to make only one value from the dropdown read only so it can't be edited, and leave the rest editable
I want if possible to do it in my view's get_form
here's what I'm doing so far :
form.fields["groups"].queryset = models.Group.objects.all()
form.fields["groups"].initial = models.Group.objects.filter(is_default=True)
So basicly I want the default value to always be selected ( selected and disabled )
Thanks for guiding me through this.
You can disable the field by setting the disabled attribute [Django-doc] to True:
form.fields['groups'].queryset = models.Group.objects.all()
form.fields['groups'].initial = models.Group.objects.filter(is_default=True)
form.fields['groups'].disabled = True
If you set this while constructing the form (in the __init__ method), it will also prevent people from making "forged" POST requests with a different value. In other words: this does not only disables the field at the browsers client, but also prevents malicious requests.

Data Model persistence between requests

I'm building an application using Flask and Flask-SQLAlchemy.
In the application I use database models written in the SQLAlchemy declarative language, let's say that I have a table called Server.
The application, by design choices, ask the user -via WTForms- to set values for the fields of the Server table between different pages(views) and I need save the instance to the database in the last view.
My problem is: I have 2 'circular' views and would like to store the instances of objects created in the first view directly in the database session in order to be able to query the session in the second view and committing the result only in the last view (the end of the loop), like the pseudocode shows (very simplified, it makes no sense in this form but is to explain the concept):
def first_view():
form = FormOne() #prompt the first form
if form.validate_on_submit():
#form validate, i create the instance in SQLalchemy
server = Server() #Database Model
server.hostname = form.hostname.data
loop_count = form.repetition.data
session['loop'] = loop_count
db.session.add(server) #adding the object to the session
#but i'm not committing the session in fact the next view needs to know about this new object and a third view needs to commit based on the user input
return redirect(url_for('second_view'))
return render_template("first_view.html", form=form)
def second_view():
form = FormTwo() #prompt the second form
if form.validate_on_submit():
hostname_to_search = form.hostname.data #i get some other input
#i use the input to query the session (here different server instace can appear, depends on the user input of the first view)
rslt= db.session.query(Server).filter(Server.hostname==hostname_to_search ).all()
#the session is empty and doesn't contain the instance created in the previous view... <-- :(
if session['loop'] <= 0 :
#end the loop
return redirect(url_for('commit_view'))
else:
loop_count = session.pop('loop',1)
session['loop'] = loop_count-1
#go back to the first page to add another server instance
return redirect(url_for('first_view'))
return render_template("first_view.html", form=form)
def commit_view():
#loop finished, now i can commit my final instances
db.session.commit() <--here i save the data, db.session is empty
return 'DONE!'
but SEEMS that the session in Flask-SQLAlchemy is local to the request, so it appears that between a view and another the db.session is resetted/empty.
The first solution came into my mind is to store even the server object values in the flask.session (in json format) but this means that I need to jsonify and parse back every time the flask.session to build back the objects in each view: I cannot use the query power of the database, but for example I have to manually check if the hostname input by the user is already present in the previously created server objects.
My question is: how is possible(if it is possible and good practice) to keep the db session 'open' between different views:
How can i implement this?
Is it convenient?
Is it thread safe?
Maybe i'm using the wrong approach to solve the problem? (Of course I can do a single page, but the real case in much more complex and needs to be structured between different pages)

Django serving stale form validation choices

I have a series of views that builds a set of associations and then triggers an action based on that association.
For validation, I've built a custom function in forms.py and use that to populate the initial choices/validation values.
def targets():
o = []
for r in Target.objects.all():
o.append((r.pk, r.host.statline()))
return o
class StageForm(forms.Form):
targets = forms.MultipleChoiceField(choices=targets(), widget=forms.CheckboxSelectMultiple())
In the view, I calculate a new list pertinent to the Build Object, checking to make sure all the required parameters have been defined, and submit that as the presented choices.
hl = Target.objects.filter(build=b)
cl = []
for h in hl:
if h.host.ready():
cl.append((h.pk, h.host.statline()))
form.fields['targets'].choices = cl
The problem I'm seeing is that if I add a new host to the environment, the form operates as I expect and presents the hosts ready for a build, but then the presented option fails validation.
The list I generate from the build and the list I generate for validation is coming from the same place, and the new host is clearly in the table, but it doesn't present to the validation until I restart the development server.
To add to the strangeness, I get the expected results from the manage.py shell while the form is still validating with stale data.
Is this just because I'm running on the development server, or is my methodology here just untenable and should be switched to overriding the __init__ method?
You should be using ModelMultipleChoiceField with a queryset.
class StageForm(forms.Form):
targets = forms.ModelMultipleChoiceField(queryset=Target.objects.all(), widget=forms.CheckboxSelectMultiple())
(If the Target string representation does not output the correct display value, you can subclass the field and define label_from_formset, as shown in the docs.

Django session data obtainable from models.py without key

I want to access a session variable from a model, in order to customize what gets shown in a template using {{item.seller_email}}. relevant code from models.py:
SESSION_STORE = import_module(settings.SESSION_ENGINE).SessionStore
class Item(Action):
def seller_email(self):
seller = self.get_seller()
session = SESSION_STORE()
debug('session: %s' % vars(session))
viewer = fetch(Client, email = session.get('client', None))
administrator = viewer.is_admin() if viewer else False
debug('viewer: %s, administrator: %s' % (viewer, administrator))
if administrator:
return seller.email
else:
return seller.anonymized[16:]
the answer here seems to say that this is not possible nor desired, but I lack the understanding of Django's session mechanism to be sure: Access session / request information outside of views in Django
this seems to indicate that the current session can be retrieved outside of views, though, just by not passing a key: https://docs.djangoproject.com/en/1.6/topics/http/sessions/#using-sessions-out-of-views, but I've tried it both that way and the above way with the same results: an "empty" session is created.
I'm sure there are other, better ways of doing this, and I will find one myself, but can someone familiar with Django internals explain why this is not possible?
Because the model shouldn't know anything about session, that creates unnecessary entanglement(?) beetween different components and violation of MVC pattern.
What I suggest to do, is to call the function, specifying parameters needed to get a proper result, viewer object in this case.
If using the code in the question, how do you expect your model to work if it's used in a management command (where there is no http request and as a result no session) ?
Or from a celery job ?

Django - accessing the RequestContext from within a custom filter

I've got a filter currency, which takes a value in USD and converts it to a currency (either USD or GBP). The currency to convert to is stored in the session, but filters don't take RequestContext, so I can't grab it straight from there.
Is there a better way than passing the relevant session element into the template, and from the template into the filter as an argument? Whilst this approach is working, it seems fairly horrible, and I'm likely to end up passing the currency to (almost) every template.
My filter currently looks something like this:
def currency(value, currency):
if currency == 'usd':
val = '$%.2f' % value
return mark_safe(val)
d = Decimal(value)
val = '£%.2f' % (d*Decimal('0.63'))
return mark_safe(val)
If you create a template tag instead of a filter, you are given the context to work with (which contains the request). http://docs.djangoproject.com/en/dev/howto/custom-template-tags/#writing-custom-template-tags
I would have to agree with Adam that migrating the code to a custom tag is the best way.
However, a client needed to record the use of certain filters only when a page was published and had a HUGE inventory of templates that used the existing filter syntax. It would have been a costly undertaking to rewrite all the templates. So, I came up with this simple function that extracts the context from the call stack:
https://gist.github.com/drhoden/e05292e52fd5fc92cc3b
def get_context(max_depth=4):
import inspect
stack = inspect.stack()[2:max_depth]
context = {}
for frame_info in stack:
frame = frame_info[0]
arg_info = inspect.getargvalues(frame)
if 'context' in arg_info.locals:
context = arg_info.locals['context']
break
return context
Be sure to read my warnings, but this DOES give standard filters access to the context (when it is available) WITHOUT having to turn your filter into a tag.
This can be done using a filter. First make sure that you have "django.core.context_processors.request" in you TEMPLATE_CONTEXT_PROCESSORS. If you don't, you can add this to your settings.py file:
TEMPLATE_CONTEXT_PROCESSORS += (
"django.core.context_processors.request"
)
Then in your template, your filter will look like this (assuming your session variable is named 'currency_type'):
{{value|currency:request.session.currency_type}}
Or is something like this what you are considering fairly horrible?
A somehow less hacky solution to Daniel Rhoden's proposal is, to use threading.local(). Define a middleware class, which stores your request as a global object inside your local thread, and add that class to your MIDDLEWARE_CLASSES.
Now a template filter can easily access that request object.

Categories

Resources