Exposing global data and functions in Pyramid and Jinja 2 templating - python

I have a base template for when a user is logged in, and on that base template, I need to add user specific options in a drop down menu. This drop down menu with options must be constant across all handlers, i.e., any time the base template is invoked (extended) with a child template.
Other than performing the necessary DB query, assigning the query results to a variable, and passing that variable to every handler (there are many), how can I consolidate this into one query and one variable, which gets passed directly to the base template? I am using jinja2 templates as well.
I would hate to do something so cumbersome in exchange for something far more simple and maintainable.
Any ideas? Thanks.
EDIT
So I still haven't found anything that's exactly what I'm looking for; however, I decided to at least make some headway in the interim. So, I made a custom decorator that takes a view's returned dict() and appends the appropriate data to it. For example:
def get_base_data(func):
def wrapper(request):
d = func(request)
user_id = request.user.id # used in query
contact_group_data = ContactGroups.query.filter(...criteria...).all()
d['contact_group_data'] = contact_group_data
return d
return wrapper
Now, I can at least decorate each method very concisely and simply by putting:
#view_config(...)
#get_base_data
def my_handler(request):
pass # rest of code...

This is one of most inobvious things in Pyramid and took a while to find for me, too.
You can modify the global template context in BeforeRender event.
http://docs.pylonsproject.org/projects/pyramid/en/latest/narr/hooks.html#using-the-before-render-event
Alternatively, you could use class based views, inherit all your views from one base view class which has get_base_data(), then the class instance is passed to the template context to all your views and then you could extract the data with {{ view.get_base_data }}.
http://ruslanspivak.com/2012/03/02/class-based-views-in-pyramid/
I vouch for the latter approach as it is more beautiful, predictable and easier to maintain engineering wise.

Related

Solution needed to a scenario

I am trying to make use of a column's value as a radio button's choice using below code
Forms.py
#retreiving data from database and assigning it to diction list
diction = polls_datum.objects.values_list('poll_choices', flat=True)
#initializing list and dictionary
OPTIONS1 = {}
OPTIONS = []
#creating the dictionary with 0 to no of options given in list
for i in range(len(diction)):
OPTIONS1[i] = diction[i]
#creating tuples from the dictionary above
#OPTIONS = zip(OPTIONS1.keys(), OPTIONS1.values())
for i in OPTIONS1:
k = (i,OPTIONS1[i])
OPTIONS.append(k)
class polls_form(forms.ModelForm):
#retreiving data from database and assigning it to diction list
options = forms.ChoiceField(choices=OPTIONS, widget = forms.RadioSelect())
class Meta:
model = polls_model
fields = ['options']
Using a form I am saving the data or choices in a field (poll_choices), when trying to display it on the index page, it is not reflecting until a server restart.
Can someone help on this please
of course "it is not reflecting until a server restart" - that's obvious when you remember that django server processes are long-running processes (it's not like PHP where each script is executed afresh on each request), and that top-level code (code that's at the module's top-level, not in a function) is only executed once per process when the module is first imported. As a general rule: don't do ANY db query at a module's top-level or at the top-level of a class statement - at best you'll get stale data, at worse it will crash your server process (if you're doing query before everything has been properly setup by django, or if you're doing query based on a schema update before the migration has been applied).
The possible solutions are either to wait until the form's initialisation to setup your field's choices, or to pass a callable as the formfield's choices options, cf https://docs.djangoproject.com/en/2.1/ref/forms/fields/#django.forms.ChoiceField.choices
Also, the way you're building your choices list is uselessly complicated - you could do it as a one-liner:
OPTIONS = list(enumerate(polls_datum.objects.values_list('poll_choices', flat=True))
but it's also very brittle - you're relying on the current db content and ordering for the choice value when you should use the polls_datum's pk instead (which is garanteed to be stable).
And finally: since you're working with what seems to be a related model, you may want to use a ModelChoiceField instead.
For future reference:
What version of Django are you using?
Have you read up on the documentation of ModelForms? https://docs.djangoproject.com/en/2.1/topics/forms/modelforms/
I'm not sure what you're trying to do with diction to dictionary to tuple. I think you could skip a step there and your future self will thank you for that.
Try to follow some tutorials and understand why certain steps are being taken. I can see from your code that you're rather new to coding or Python and there's room for improvement. Not trying to talk you down, but I'm trying to push you into the direction of becoming a better developer ;-)
REAL ANSWER:
That being said, I think the solution is to write the loading of the data somewhere in your form model, rather than 'loose' in forms.py. See bruno's answer for more information on this.
If you want to reload the data on each request that loads the form, you should create a function that gets called every time the form is loaded (for example in the form's __init__ function).

Generic views for updating objects?

How do I rewrite the function-based view that only updates an object into a class-based view? Something like this (wrote this for a tutorial).
from django.contrib.auth.decorators import permission_required
#permission_required("catalog.mark_returned")
def mark_returned(request, pk):
bk = BookInstance.objects.get(pk=pk)
if bk.status == "a":
#some tests here
bk.status = "a"
bk.due_back = None
bk.borrower = None
bk.save()
return redirect("on-loan")
And in general, does it make sense to use generics for things like that?
Because currently, I only use the generic list and detail views.
Sorry for beginner-level questions:)
It's a view without a template or a form; that kinda excludes the usefulness of just about every generic view except the basic View class itself.
You would override the ClassBasedView's dispatch method (since the request method does not matter) with the logic of your function view.
The pk value can be accessed through self.args[0] or self.kwargs['_name_of_captured_variable_here'].
Does it make sense? In general, I would say becoming familiar with CBVs and especially the generic ones will do you good in the long run. In your special case, however, you gain or lose nothing by using a CBV over function view. The view is just too simplistic.

flask_admin change inline_models behaviour

I want to change some existing Python code that uses flask_admin. One of the views uses inline_models with the (ClassName, Options) declaration pattern. The inlined class has, amongst others, a text field.
I want to change the flask_admin default behaviour in the following ways:
I want to make the text field read-only. I.e. still display it, but prevent the user from changing existing content.
I do not want to allow users to delete instances of the inlined class, i.e. I want to get rid of the "Delete?" checkbox next to every entry.
I want to override the default "Add Item" button behaviour with some custom JavaScript.
I did some Googling around but anything that looked potentially promising also looked very non-trivial. I'm hoping for some reasonably straight forward way to achieve this.
Your help would be much appreciated.
Yeesh. It looks like we're out in poorly-documented territory, here. It's hard to know if I'm improving on what you've already found, but I'll hope you're looking for something easier than writing a custom administrative view template.
Following the calls, it looks like the options dictionary eventually gets passed to the constructor of InlineBaseFormAdmin where the various form_* keys are extracted and applied (not sure all are respected, but I see at least form_base_class, form_columns, form_excluded_columns, form_args, form_extra_fields, form_rules, form_label, form_column_labels, form_widget_args). I think you can accomplish what you need via form_widget_args, but you can probably also get there via form_rules or by overriding InlineBaseFormAdmin's get_form or postprocess_form methods:
class SomeModelView(MyBaseModelView):
...
inline_models = [(db.SomeOtherModel, {
"form_widget_args": {
"uneditable_field_name": {"readonly": True}
}
})]
...
The delete option can be controlled by providing your own inline form model to override display_row_controls:
from flask_admin.contrib.sqla.form import InlineModelConverter
from flask_admin.contrib.sqla.fields import InlineModelFormList
class CrouchingTigerHiddenModelFormList(InlineModelFormList):
def display_row_controls(self, field): return False
class MyInlineModelConverter(InlineModelConverter):
inline_field_list_type = CustomInlineModelFormList
#adding to above example
class SomeModelView(MyBaseModelView):
...
inline_model_form_converter = MyInlineModelConverter
inline_models = [(db.SomeOtherModel, {
"form_widget_args": {
"uneditable_field_name": {"readonly": True}
}
})]
...
NOTE: The widget args, such as readonly, are getting passed on to wtforms as render_kw, but at a blush the WTForms docs aren't clear that these get expressed as attributes in the resulting HTML input element (so any HTML input element attributes are valid here).
It looks like form.js controls this behavior, so you should be able to monkey-patch its addInlineField method to execute your own code before or after the model addition. You could override the create and/or edit templates for this--but if you're using flask-admin 1.5.0+, this might be as simple as adding extra_js = ["your-custom.js"] to the view class (caution: it looks like this script gets included on every page for this view).

Is it possible to check whether a context variable is already set in a view within a custom context processor definition?

The issue is that in some views, I am manually getting a context variable (let's say "G") of interest since I use it to find other information in that particular view (i.e.views A,B,C), but in other views (i.e. X,Y,Z), I need to get that particular context variable since this context is to be available in every single view in my project (since my base template uses the context variable). The issue with using a custom context processor is that I believe it will make an additional and IDENTICAL DB call in views (A,B,C) since those views are already getting that context variable since it's needed to get other data in the view. What I was thinking was maybe I could implement a context processor that checks whether that specific context variable is set for a given request. Is this possible? Is there an easier solution? The code below may clarify the issue for some people.
Thank you for any advice!
def viewA(request):
g=G.objects.get(user=request.user)
posts = Post.objects.filter(g=g)
return direct_to_template(request,'something.html',{'G':g, 'posts':posts})
def viewX(request):
stuff = Albums.objects.get(user=request.user)
return direct_to_template(request,'something2.html',{'stuff':stuff})
def my_context_processor(request): #redundant in case of viewA (hits db again?)
return {'G':G.objects.get(user=request.user)}
def ideal_processor(request):
#check context vars to see if G is already in there
#if it is, return {}, else, return {'G':G.objects.get(user=request.user)}
def always_G(request):
if not hasattr(request, 'G'):
{'G':G.objects.get(user=request.user)}
I just made middleware that sets the variabel G to request.G since I need it on virtually every request anyway. i.e.:
class GuildMiddleware(object):
def process_request(self, request):
request.G = figure_out_what_G_is()
return None
Now you can use request.G anywhere in your views (and templates if you're using direct_to_template, RequestContext, etc.).

Django 1.1 FormWizard, Dynamically extend form

I am trying to create a multipage form, where the number of field elements on the second page is defined by the answers given on the first.
I have a formWizard set up and my understanding is that I need to use process_step() to alter the setup for the next page. I can either extend an existing form definition to add more elements, or merge 2 or more form definitions together to produce the correct number of form elements, but i have no idea how to do this.
Eg
Page 1 - Select interested subjects:
Page 2 - for each subject: ask relevant questions. Questions are defined as seperate forms in application, but need to be shown on one page, or merged into a single form.
Any help much appreiciated.
Spender
Spender,
At least at the moment I don't know a way of merging multiple forms onto one page in a FormWizard. In django 1.2 you will be able to include FormSets as steps in FormWizards (as per this ticket) but those only deal with multiple copies of identical forms, not compilations of many forms. But there is a way to do what you ask:
from django.contrib.formtools.wizard import FormWizard
from django import forms
class SubjectForm(forms.Form):
subjects = forms.MultipleChoiceField(choices = (('language', 'language'),
('sport','sport')))
class RelatedQForm(forms.Form):
"""Overload the __init__ operator to take a list of forms as the first input and generate the
fields that way."""
def __init__(self, interested_subjects, *args, **kwargs):
super(RelatedQForm, self).__init__(*args, **kwargs)
for sub in interested_subjects:
self.field[sub] = forms.CharField(label = "What do you think about %s" % subject)
class SubjectWizard(FormWizard):
def done(self, request, form_list):
process_form_list(form_list)
def process_step(self, request, form, step):
if step == 1:
chosen_subs = form.cleaned_data['subjects']
self.form_list[1] = RelatedQForm(chosen_subs)
With this code you instantiate your FormWizard as you normally would in the view and then let the wizard class take care of everything behind the scenes.
The general idea is to overload the init class of a "RelatedQForm" to dynamically alter the fields. This code snippet was taken from here. You can make the processing within the init operator as complex as you'd like, read "include the fields from your forms as if-elif blocks inside the for-loop" ... you could probably even figure out a way to strip the fields from your current forms programatically, I'd have to see them to figure it out though.
Your "process_form_list" function will need to loop over the fields using something like:
for field, val in form.cleaned_data.items():
do_stuff
Hope this gets you on your way :)
I don't think the
self.form_list[1] = RelatedQForm(chosen_subs)
part works. I always get the error message:
object is not callable
It seems to be like form_list only accepts RelatedQForm (the name of the form), not an instance of it.

Categories