How to force webapp2_cached_property to re-evaluate - python

I know that webapp2_cached_property replaces a method with data after the first call for each call to it thereafter, and thus my problems arise.
I have a multilingual site and I am using a form to build some simple select menus. The select menus are data that change language. Obviously if a user changes the language on the system I would like to rebuild the form. However, I don't want to remove the webapp2_cached_property as it will then rebuild the form every time the user calls the same url and that will slow down the system. So does anyone know a way to force webapp2_cached_property to re-evaluate on demand, e.g. when client changes language. At the moment I have everything else in the selected language but the select data in the default language. Anything down and dirty will do! Ah, yes this only happens on production and not on the dev server...
class HomeRequestHandler(BaseHandler):
"""
Handler to show the home page
"""
def get(self):
params = {}
params['products'] = []
params['max_searches'] = 1
params['user_search_count'] = 0
return self.render_template('index.html', **params)
#webapp2.cached_property
def form(self):
import product.product_data.forms as forms
return forms.ProductForm(self)
Ok, I have tried the following but still the language is not changing in production...
Added this to my base handler - it's working!
if hasattr(self, 'form'):
if self.locale != self.oldLocale and hasattr(self, 'form_no_cache'):
new_form = self.form_no_cache
kwargs['form'] = new_form()
logging.info('reset form')
logging.info(kwargs['form'].product_type())
else:
kwargs['form'] = self.form
logging.info('using cached form')
And added this to my home handler
def form_no_cache(self):
import product.product_data.forms as forms
return forms.ProductForm(self)
All this is fine in development and the logs seem correct in development and production...
Any ideas people?

ok, couldn't figure this one out, so rewrote the code to build selects in a simple def and pass as a parameter instead of form. Seems to be working. I have my suspicions regarding wtforms though, but it's a pain not knowing, but no time...

Related

django - allow user to set something in database

I want to let user change language of a site by click on a country flag.
In my models.py file i have:
class RegularUser(models.Model):
(...)
language = models.CharField(max_length = 10, null=True, blank=True, choices=[(a, _(b)) for (a,b) in settings.LANGUAGES], verbose_name=_("Language"))
I also made there a function:
def set_language(self, new_lang):
lllang = self.language
lllang = new_lang
In my html file i have:
function setLang(lang){
language = document.getElementById("language")
lang_form = document.getElementById("language_form")
language.value= lang
lang_form.submit()
{{request.user.get_profile.set_language(lang) }}
}
This function in setted to some button as "onclick".
This should do two thing:
1. Change immediately the language (it's done)
2. Change uset language in the database, so next time when we will log in - it will be as we set it now (it doesn't work :( )
I'm getting an error such:
TemplateSyntaxError at /
Could not parse the remainder: '("lang")' from 'request.user.get_profile.set_language("lang")'
Just to tell - if I'll put :
{{request.user.get_profile.get_language}}
Then I'm getting code of my language, so it's fine.
get_language function from models.py:
def get_language(self):
if self.language:
return self.language
elif self.location and self.location.country:
return self.location.country.iso.lower()[:2]
else:
return None
Umm ... any help?
Am I just giving the "lang" in a wrong way, or I don't get something in Django?
In a Django template, you cannot call a Python function with parameters. See https://docs.djangoproject.com/en/1.4/topics/templates/#variables . That's why you get no error message when you write {{request.user.get_profile.get_language}} (actually, your function is called without parameters, and the resulting exception is swallowed by Django).
Also, you cannot use the lang parameter, which is a client-side JavaScript function parameter, in a Python function on the server side. Try calling the function using AJAX - you'll need to add another view function for this, and I strongly suggest you don't use bare JavaScript, but a library like jQuery.
For your actual problem (letting the user set his preferred language), there's already a function for that in Django's internationalization package.

Make a Class Method "Template" in GAE Python

I'm still quite new to python with less than a year of experience, and I've been learning it through building a rather large project on google app engine. It's grown to be a behemoth of 10k+ lines of code and html templates, so I'm in the process of refactoring a rather large portion of the code to follow a much more rigorous MVC architecture.
My question is one concerning python directly. I don't know the words to say exactly what I want to build, so I would just like to use an example.
This is my current "basic" code for displaying a view:
class MainHandler(webapp.RequestHandler):
def get(self):
tUser = users.get_current_user()
tContext = {
'user': tUser,
'login': users.create_login_url(self.request.uri),
'logout': users.create_logout_url(self.request.uri),
}
#User is logged in
if (tUser):
#code for loading view information and adding to the context dict
#CUSTOMIZATION GOES HERE
else:
self.redirect("/")
tmpl = os.path.join(os.path.dirname(__file__), 'logged-in.html')
self.response.out.write(render(tmpl, tContext))
I would like to take this boilerplate code and abstract it somehow, maybe with a way of prepending/appending the "customizable" code for each class method?
I think I might be able to use a decorator to do this somehow, but I have no python mentors outside of stackoverflow to point me in the right direction. I would prefer the most pythonic method possible, or at least what's generally considered "best practices" in this situation.
The python version is 2.7.2.
edit
Note, if I can do this with decorators, then what is necessary for me to be able to call the decorator from an entirely different class and python file? I would like to be able put my decorators in one file and reference it from elsewhere so my code is as normalized as is reasonable. =)
edit 2
This is the testing code that I worked out in the console, and I have to leave for the evening or I would refine it more. However, it appears that this successfully accesses and modifies the class's properties, which is pretty much what I think you need to pull this off in GAE.
class Decorators():
#staticmethod
def BeginInit(pInFunction):
def OutFunction1(self):
print str(self.innerv)
pInFunction(self)
return OutFunction1
#staticmethod
def EndInit(pInFunction):
def OutFunction2(self):
self.innerv = 5
pInFunction(self)
print "this is test 3"
return OutFunction2
class Test2Handler():
innerv = 10
#Decorators.BeginInit
#Decorators.EndInit
def TestPrint(self):
print self.innerv
print "this is test 2"
Prints
10
5
this is test 2
this is test 3
Instead of using decorators, you could use a base class for your request handlers, like so
class HandlerBase(webapp.RequestHandler):
def get_context(self):
return {}
def get(self):
tUser = users.get_current_user()
tContext = {
'user': tUser,
'login': users.create_login_url(self.request.uri),
'logout': users.create_logout_url(self.request.uri),
}
# tContext holds the defaults for your context
#User is logged in
if (tUser):
# if the dict returned by self.get_context() contains a key
# that's already in tContext, tContext[key] will be overridden
# with self.get_context()[key]
tContext.update(self.get_context())
else:
self.redirect("/")
tmpl = os.path.join(os.path.dirname(__file__), 'logged-in.html')
self.response.out.write(render(tmpl, tContext))
class MainHandler(HandlerBase):
def get_context(self):
# the contents of the following dict will be added to
# the context in HandlerBase
return {'greeting': 'Hi!'}

Django signals on GAE with django-nonrel

I am using django-nonrel for my project on GAE. My requirement is that in my application at a time only one user should login with the given username. I tried to implement the following suggested approaches:
Allow only one concurrent login per user in django app and How can I detect multiple logins into a Django web application from different locations?
But the problem is that both of the approaches working on the development server but didn't work on google app engine. So I switched to django-signals as my alternate approach. I created one post_login signal which will store the username for every login user in a table Visitor in database. On every logout,other signal post_logout will remove the user from this table.The part of codes are as:
#signals.py
post_login = django.dispatch.Signal(providing_args=['request', 'user'])
post_logout = django.dispatch.Signal(providing_args=['request', 'user'])
#models.py
def login_handler(sender,user, **kwargs):
try:
result=Visitor.objects.get(user=user)
print "You already have login with your name"
except:
visitor=Visitor()
visitor.user=user
visitor.save()
post_login.connect(login_handler)
def logout_handler(sender,user, **kwargs):
try:
result=Visitor.objects.get(user=user)
result.delete()
except:
return False
post_logout.connect(logout_handler)
#django.contrib.auth.__init.py__
def login(request):
:
user_logged_in.send(sender=user.__class__, request=request, user=user)
post_login.send(sender=None,request=request, user=user)
def logout(request):
:
user_logged_out.send(sender=user.__class__, request=request, user=user)
post_logout.send(sender=None,request=request, user=user)
Please note that I am getting the following error while running my application on google app engine.
Error: Server Error
The server encountered an error and could not complete your request.
Also I am not able to login into Admin part of the application. Please help me to find right approach to implement this requirement or let me know where I am doing wrong.
Thanks for your patience for reading this huge problem description :-)
1.
You should not be editing the django framework like you are doing. Don't touch the files inside django.contrib.auth
If you wish to send a signal after someone is logged in, then send the signal in your view where you log the person in
2.
Not sure what your actual error is because you are not displaying it (if this is a dev environment set DEBUG = True to get a better stack trace) But by lookingat you code, you are not grabbing the arguments correctly in the signal handler. It should look more like this:
def login_handler(sender, **kwargs):
try:
user = kwargs['user']
request = kwargs['request']
result=Visitor.objects.get(user=user)
print "You already have login with your name"
except:
visitor=Visitor()
visitor.user=user
visitor.save()
post_login.connect(login_handler)

App engine datastore query issue

I have a weired problem with couple of queries I am trying to run.
I have built a method which returns a tuple of result from the query-
def get_activeproducts():
query = Product.gql("WHERE active = True")
choices = []
for obj in query:
choices.append((str(obj.key()), obj.name))
return choices
The problem is, the result is same for each call. Even if products are deleted or changed to 'False' in the product attribute 'active'. The result will be refreshed only when I restart the sdk server. In production, it just doesnt change till I change versions.
I have seen similar issue with one more query where the query property is BooleanProperty.
Any idea on how this could be fixed?
EDIT:
I am using the method in a tipfy application. It is used to populate a select field in wtforms. 'choices' basically takes in a list of tuples (value, name) pair.
class InvoiceForm(Form):
product = SelectField('Product', choices=get_activeproducts())
I dont have any issue with editing. WHen I check it from the admin end, I can see that certain products are set to 'False'. And even if I empty(delete) the whole list of products, I get the same list I got the first time.
I am not using caching anywhere in the application.
Your class definition is getting cached by the App Engine runtime when an instance is started, with the default set to what it was when the instance started. To make the choices dynamic, you need to set them at runtime.
Example from the wtforms (which IIRC is what tipfy is using) docs; will need to be adjusted for App Engine queries:
class UserDetails(Form):
group_id = SelectField(u'Group', coerce=int)
def edit_user(request, id):
user = User.query.get(id)
form = UserDetails(request.POST, obj=user)
form.group_id.choices = [(g.id, g.name) for g in Group.query.order_by('name')]
when you create your form, the function is called once.
you can overload the form __init__.py function to do this cleanly
class InvoiceForm(Form):
product = SelectField(u'Group', choices=[])
def __init__(self, product_select, *args, **kwargs)
super(InvoiceForm, self).__init__(*args, **kwargs)
self.product.choices = select_dict
----
form = InvoiceForm(product_select=get_activeproducts())

files getting wrongly cached by the web server while using standard python file operations (Django)

I have a Django app, which populates content from a text file, and populates them using the initial option in a standard form. The file gets updated on the server, but when the form gets refreshed, it picks up content from the a previously saved version, or the version before the Apache WebServer was reloaded.
This means that the file is getting cached, and the content is picked up from a wrong cache and not the new file.
Here is my code. How do I ensure that everytime, spamsource function picks up the content from the most recently saved file, instead of from a cache.
def spamsource():
try:
f= open('center_access', 'r')
read=f.read()
# some manipulation on read
f.close()
return read
except IOError:
return "prono.nr"
class SpamForm(forms.Form):
domains =forms.CharField(widget=forms.Textarea(attrs=attrs_dict),
label=_(u'Domains to be Banned'), initial= spamsource())
def function(request):
# It writes the file center_access based on the changes in the textbox domains
The issue is simply that all the parameters to fields are evaluated at form definition time. So, the initial value for domains is set to whatever the return value is from spamsource() at the time the form is defined, ie usually when the server is started.
One way of fixing this would be to override the form's __init__ method and set the initial value for domains there:
class SpamForm(forms.Form):
domains = ...
def __init__(self, *args, **kwargs):
super(SpamForm, self).__init__(*args, **kwargs)
self.fields['domains'].initial = spamsource()
Alternatively, you could set the initial value when you instantiate the form in your view:
form = SpamForm(initial={'domains': spamsource()})
I fixed this. The problem was the presence of all the caching middleware in settings.py, which were being used to speed up another side of the web app.

Categories

Resources