Using arrays in django views.py code - is it a good habit? - python

I have couple of functions which captures ajax requests. I get the data from the request and put into the array. Then I loopthru this array in another function for instance to pass the data to context. This solution basically works, but is it a good way of doing things ? Appreciate for any feedback.
piece of code:
user_choices = []
#login_required
#csrf_exempt
def make_order(request):
if request.method == "POST" and request.is_ajax():
data = json.loads(request.body)
for order in data["array"]:
user_choices.append(order)
return HttpResponse(200)
else:
return redirect(request, 'home')
movie = 0
seats = []
#login_required
def confirmation(request):
if len(user_choices) > 0:
movie = Movies.objects.get(pk=int(user_choices[0]["id"]))
for seat in user_choices:
seats.append(seat["row"]+":"+seat["seat"])
context = {
"movie":movie.title,
"seats":seats
}
return render(request, "main_templates/confirmation.html", context)
else:
return redirect("home")

I have couple of functions which captures ajax requests. I get the data from the request and put into the array. Then I loopthru this array in another function for instance to pass the data to context. This solution basically works, but is it a good way of doing things?
No, you introduce an anti-pattern called global state [se]. Global state is a serious anti-pattern because it makes a program unpredictable. Depending on previous requests, the list can already contain data. Normally a GET request should have no side-effects. By altering lists, that is no longer the case.
Furthermore in this specific case, it means that if one user queries for data, and then another user makes a request for data, the data of the first user "leaks" to the second user.
But nevertheless, even if you manage to make it more safe, a global state introduces a lot of difficulties. These are discussed in the software engineering post. While you can of course each time aim to manually fix these issues, it will result in a lot of work and bugs, and therefore more trouble than it is worth.

Related

Can you return a return statement in Python?

I'm working on a web application and I'm creating a login & authentication form. Some pages should be off-limits if you are logged in (e.g. the login form), and I'm trying to create a systematic way of doing this.
To do this, I was trying to create a function which would return a return statement. See the code below.
def login():
redirect_to_home_if_logged_in()
# rest of login page
def register():
redirect_to_home_if_logged_in()
# rest of register page
def redirect_to_home_if_logged_in():
if current_user.is_authenticated:
print("squa")
return return redirect(url_for('home'))
I was hoping that the function would return a return statement, so that the login and register pages would return a redirect. Instead I got an error.
No, you cannot return a return statement as such.
A common arrangement is to refactor the code so that the caller takes care of what you return, and relays that back to its caller.
Another approach is via callbacks. Something like this:
def bar():
def foo():
return "ick"
return foo
Here, the return value from bar is a function object foo. This requires the caller of bar to know this and agree to call foo() at some later point in time when the value is actually required. (You could often also use a lambda in these circumstances; Python predictably calls this type of value a "callable".)
As noted by #Dair in a comment already, what you are actually trying to accomplish is probably already covered by a completely different mechanism by whichever web framework you are probably already using. Here's Flask and here's Django.

ValueError: View didn't return an HttpResponse object. It returned None instead

def frontblog(request):
if request.method=='POST':
for post in Posts.objects(tags=request.POST('search')):
posttitle=post.post_title
postcont=post.post_content
postdate=post.post_date
posttag=post.post_tags
return render_to_response("frontblog.html",
RequestContext(request,
{'post':post}))
I have tried to send the data from mongo db database as by search using tag post get retrieved and should be send to display on html page.
NB : answer based on badly indented code, so it's a bit of a guessing game... but if you want a correct answer learn to post correctly indented code.
You start your code with a test on request.method=='POST' and everything else is under that branch, which means that if it's a GET request (or PUT or whatever) your view function implicitely returns None.
There are quite a few other WTFs in your code but fix this one first. BTW a "search" should be done as GET request, not POST. Also, in a POST request, request.GET will most probably be empty. Finally, you DO want to use a Form to sanitize user inputs... Well, unless you don't mind your site or app being hacked by the first script-kiddie, that is.

Exposing global data and functions in Pyramid and Jinja 2 templating

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.

Django posts and responses

This is less a technical question, more a "am i doing this in the right way" question.
I have several models defined
class Style(models.Model):
tag_xml = models.TextField()
image = models.ImageField(upload_to="styles")
user = models.ForeignKey(User)
uploaded = models.DateField()
class StyleMatch(models.Model):
style = models.ForeignKey(Style)
item = models.ForeignKey(FashionItem)
they can't be populated via html forms simply due to the nature of the task, so in order to populate them I have a html page with jquery and lots of event functions and other javascript goodies. When the save button is clicked I call .ajax() and pass all of the collected variables
var saveRequest= $.ajax({
url: "/save_style/",
type: "POST",
data: "selection="+s+"&user="+user+"&src="+image_src,
dataType: "text"
});
My save_style view then saves the values into the model
def save_style(request):
if request.method == 'POST':
selection = request.POST['selection'].rsplit("|")
user = request.POST['user']
src = request.POST['src']
f = open(MEDIA_ROOT+src)
image_file = File(f)
u = User.objects.get(id=user)
style = Style(tag_xml = "",
image = image_file,
user = u,
uploaded = date.today())
style.save()
for s in selection:
if (s != ''):
match = FashionItem.objects.get(id=s)
styleMatch = StyleMatch(style = style,
item = match)
styleMatch.save()
i = StyleMatch.objects.filter(style=style)
items = FashionItem.objects.filter(id__in=i)
return render_to_response('style_saved.html', dict(image=src, items=items, media_url = MEDIA_URL), context_instance=RequestContext(request))
After doing this I really want to go to a success page and display the records I have just added to the model, however if I use render_to_response and pass back the model details I have to rebuild the entire page in javascript, it seems better to redirect to a new template, but if I use HttpResponseRedirect a) I can't pass back values and b) it doesn't appear to be redirecting quite right (I think because the post is originating from my javascript).
So finally my questions
Is this really how I should be doing this? The django doc doesn't
really seem to cover these slightly more complicated areas, so I'm a
little unsure.
Should I be using render_to_response or
HttpResponseRedirect above? Or possibly a third option I don't know
about.
Any suggestions appreciated.
FYI I know the code above is not ideal i.e. missing validation, comments ... etc, its simply been provided for demonstration purposes. Feel free to point out any serious issues though.
Depending on the nature of your application, you probably shouldn't be building the entirety of your pages with JavaScript. However, since we're there already I've used the following solution with nice results:
Consider creating a template "fragment", as I call them. It's simply a bit of HTML that is designed to be a capsule for data transferred via AJAX. Do a render_to_response to this fragment, pass in your processed view data as variables, then retrieve this data via AJAX and use JavaScript to replace the HTML within a designated div element with the returned data.
There are some pitfalls with the above solution, such as styling and event handler attachment on the template fragment, but it should at least get you working. Just a tip in this regard, become familiar with jQuery's .on().
pass all of the collected variables
Why don't $(form).serialize()?
saves the values into the model
Why don't use django.forms.ModelForm (or few of them)?
doesn't appear to be redirecting quite right
Because redirects in AJAX are processed in AJAX call and do not affect opened page unless you process received data in JS somehow.
Also, you don't have any data validation and|or error reporting, that's bad. Actually, ModelForm should provide a huge help with that.

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.).

Categories