Google app engine ReferenceProperty relationships - python

I'm trying to get my models related using ReferenceProperty, but not have a huge amount of luck. I have 3 levels: Group, Topic, then Pros, and Cons. As in a Group houses many topics, and within each topic could be many Pros and Cons.
I am able to store new Groups nice and fine, but I don't have any idea how to store topics underneath these groups. I want to link from a page with a link "New topic" underneath each group, that takes them to a simple form (1 field for now). Obviously the URL will need to have some sort of reference to the id of the group or something.
Here are my models:
class Groups(db.Model):
group_user = db.UserProperty()
group_name = db.StringProperty(multiline=True)
group_date = db.DateTimeProperty(auto_now_add=True)
class Topics(db.Model):
topic_user = db.UserProperty()
topic_name = db.StringProperty(multiline=True)
topic_date = db.DateTimeProperty(auto_now_add=True)
topic_group = db.ReferenceProperty(Groups, collection_name='topics')
class Pro(db.Model):
pro_user = db.UserProperty()
pro_content = db.StringProperty(multiline=True)
pro_date = db.IntegerProperty(default=0)
pro_topic = db.ReferenceProperty(Topics, collection_name='pros')
class Con(db.Model):
con_user = db.UserProperty()
con_content = db.StringProperty(multiline=True)
con_date = db.IntegerProperty(default=0)
con_topic = db.ReferenceProperty(Topics, collection_name='cons')
And one function for the actual page I want to show the list of Groups, and then underneath their topics:
class Summary(webapp.RequestHandler):
def get(self):
groups_query = Groups.all()
groups = groups_query.fetch(1000)
template_values = {
'groups': groups,
}
path = os.path.join(os.path.dirname(__file__), 'summary.html')
self.response.out.write(template.render(path, template_values))
And finally the html:
<html>
<body>
New Group
<br>
{% for group in groups %}
<font size="24">{{ group.group_name|escape }}</font><br> by <b>{{ group.group_user }}</b> at <b>{{ group.group_date }}</b> {{ group.raw_id }}
<br>
<a href="/newtopic?id={{group.key.id}}" >New topice </a>
<br>
<blockquote>
{{ topics.topics_name }}
</blockquote>
{% endfor %}
</body>
</html>

Something that has side effects, such as altering the store (by creating a new object for example) should NOT be an HTTP GET -- GET should essentially only do "read" operations. This isn't pedantry, it's a key bit of HTTP semantics -- browsers, caches, proxies, etc, are allowed to act on GET as read-only operations (for example by caching results and not passing a request to the server if they can satisfy it from cache).
For modifications, use HTTP verbs such as POST (most popular essentially because all browsers implement it correctly) or for specialized operations PUT (to create new objects) or DELETE (to remove objects). I assume you'll be going to use POST to support a variety of browsers.
To get a POST from a browser, you need either Javascript wizardy or a plain old form with method=post -- I'll assume the latter for simplicity.
If you're using Django 1.0 (which app engine supports now), it has its own mechanisms to make, validate and accept forms based on models. Other frameworks have their own similarly advanced layers.
If you want to avoid "rich" frameworks you'll have to implement by hand templates for your HTML forms, direct them (via some kind of URL dispatching, e.g. in app.yaml) to a handler of yours implementing with a def post(self):, get the data from the request, validate it, form the new object, put it, display some acknowledgment page.
What part or parts of the procedure are unclear to you? Your question's title focuses specifically on reference properties but I'm not sure what problem they are giving you in particular -- from the text of your question you appear to be on the right tack about them.
Edit: the OP has now clarified in a comment that his problem is how to make something like:
"<a href="/newtopic?id={{group.key.id}}" >New topic </a>"
work. There's more than one way to do that. If the newtopic URL is served by a static form, the handler for the post "action" of that form could get back to that id= via the Referer: header (a notorious but unfixable mis-spelling), but that's a bit clunky and fragile. Better is to have the newtopic URI served by a handler whose def get gets the id= from the request and inserts it in the resulting form template -- for example, in a hidden input field. Have that form's template contain (among the other fields):
<INPUT TYPE=hidden NAME=thegroupid VALUE={{ theid }}> </INPUT>
put theid in the context with which you render that template, and it will be in the request that the def post of the action receiving the form finally gets.

Just to answer the question for others as you probably figured this out:
class NewTopic(webapp.RequestHandler):
def get(self):
groupId = self.request.get('group')
# either get the actual group object from the DB and initialize topic with topic_group=object as in 'Nick Johnson's answer, or do as follows
topic = Topic()
topic.name = self.request.get("topicname")
topic.reference = groupId
topic.put()

Thankyou for the reply.
Yeah I am aware of the get vs post. The class I posted was just to actually print all the Groups().
The issue I have is I'm unsure how I use the models to keep data in a sort of hierarchical fashion, with Groups > Topics > Pros/Cons.
Grabbing data is simple enough and I am using:
class NewGroupSubmit(webapp.RequestHandler):
def post(self):
group = Groups()
if users.get_current_user():
group.group_user = users.get_current_user()
group.group_name = self.request.get('groupname')
group.put()
self.redirect('/summary')
I need another function to add a new topic, that stores it within that group. So lets say a group is "Cars" for instance; the topics might be "Ferrari", "Porsche", "BMW", and then pros/cons for each topic. I realise I'm being a little vague, but it's because I'm very new to relational databasing and not quite used to the terminology.

I'm not quite sure what problem you're having. Everything you list looks fine - the ReferenceProperties are set up according to what one would expect from your dscription. The only problem I can see is that in your template, you're referring to a variable "topics", which isn't defined anywhere, and you're not iterating through the topics for a group anywhere. You can do that like this:
<html>
<body>
New Group
<br>
{% for group in groups %}
<font size="24">{{ group.group_name|escape }}</font><br> by <b>{{ group.group_user }}</b> at <b>{{ group.group_date }}</b> {{ group.raw_id }}
<br>
<a href="/newtopic?id={{group.key.id}}" >New topice </a>
<br>
Topics:
<ul>
{% for topic in group.topics %}
<li>{{topic.topic_name}}</li>
{% endfor %}
</ul>
{% endfor %}
</body>
</html>
To create a new topic, just use the constructor, passing in the required arguments:
mytopic = Topic(topic_name="foo", topic_group=somegroup)
Here, somegroup should be either a Group object, or a key for a Group object.

Related

Allowing language change through a query string parameter ?lang=<code> in django with i18n

In django I want to allow a query string of the form ?...&lang=&... to set the current url. Ideally the lang= would be removed from the query string, and the session language key set to the new language.
The eventual resulting url would be the url the browser lands on. This should happen for every url on the site, in addition to the other language selection methods that i18n makes available (so I guess this would be a middleware).
I rather dislike the POST to view approach that appears to be the standard for django-i18n.
Does anything like this exist already?
Django Middleware language change query string ?hl=langcode
https://gist.github.com/teury/5d2213181ed0abe06c316c19431f355e
import django
from django.utils import translation
from django.shortcuts import redirect, reverse
def is_django_greater_than_1_10():
main_version = django.VERSION[0]
if main_version > 1:
return True
sub_version = django.VERSION[1]
if main_version == 1 and sub_version >= 10:
return True
return False
if is_django_greater_than_1_10():
from django.utils.deprecation import MiddlewareMixin
superclass = MiddlewareMixin
else:
superclass = object
class LanguageMiddleware(superclass):
def process_response(self, request, response):
if request.resolver_match.view_name is "home":
lang_url = request.GET.get('hl')
if not lang_url:
return response
current_language = translation.get_language()
if lang_url == current_language:
return response
translation.activate(lang_url)
request.session[translation.LANGUAGE_SESSION_KEY] = lang_url
return redirect(reverse('home') + '?hl=' + lang_url)
else:
return response
I have implemented this system in a single "home" url
change the language according to the key value
Traslate by Google.
Idioma nativo = EspaƱol
After reading the Django documentations, and realising that the Django devs appear to also be in the business of enforcing their own coding standards (and here I am thinking specifically at this comment in the source code:
Since this view changes how the user will see the rest of the site, it must
only be accessed as a POST request. If called as a GET request, it will
redirect to the page in the request (the 'next' parameter) without changing
any state.
) I decided to go with the solution outlined in this post, which I am pasting below (almost) verbatim (hence the non semantic table layout):
<div id="languages">
<table >
<tr>
{% get_available_languages as languages %}
{% for key, item in languages %}
<td>
<form action="/i18n/setlang/" method="post">
{% csrf_token %}
<input type="hidden" name="language" value="{{key}}"/>
<input id="lang_select_{{key}}" type = "image" src="/media/img/{{ key }}.gif" alt="{{ item }}"/>
</form>
</td>
{% endfor %}
</tr>
</table>
</div>
I also came up with my own JS solution which is a little more flexible and vastly more complicated, so it's not really worth it (though it gives links: but since they need an onClick attribute, or moral equivalent, they are useless for SEO purposes).
Of course yanking the code and removing the dumbass GET limitation is also an option - though a higher maintainence one.
P.S: Those who find my comment above adversarial (and also, no doubt, my quest misguided) should consider how the stance that requires POST to switch language flies in the face of SE traversal, while forcing jumps through this kind of hoops to honor the established design convention of allowing the user to switch language by clicking on flag icons.

Changing css styles from view in Django

Sorry in advance if there is an obvious answer to this, I'm still learning the ropes with Django.
I'm creating a website which has 6 pre determined subjects (not stored in DB)
english, civics, literature, language, history, bible
each subject is going to be associated with a unique color.
I've got a template for a subject.html page and a view that loads from the url appname/subject/subjectname
what I need to do is apply particular css to style the page according to the subject accessed. for example if the user goes to appname/subject/english I want the page to be "themed" to english.
I hope I've made myself clear, also I would like to know if there is a way I can add actual css code to the stylesheet and not have to change attributes one by one from the back-end.
thanks very much!
In templates you can use conditionals for add css, like this:
<div class="{% if subject=='civics' %}civic-class{% endif %}"></div>
For this, subject value should come from view.
Now, for themed page, you could use the extends tag. Let's supose:
def your_view(request):
subject # Here you get the url subject, 'how' is up to you
if subject == 'english'
template_base = '/some/html/tenplate.html'
elif subject == 'civis':
template_base = '/some/other/template.html'
... # then you return 'template_base' variable to template
Then in template:
{% extends template_base %} # at the top
Hope this helps, is the same logic if you use Class-Based views.
Django's views are not responsible for the presentation, it's the template (and css etc of course)'s reponsability. Now assuming you have the same view serving different subjects, the view obviously need to know which is the current subject (I assume from a captured part of the url passed as argument to the view), so it can easily pass this information to the template, which in turn can use it to add a subject-specific class to the body tag. Then you only have to write your css accordingly.
As an example:
# urls.py
patterns = urlpatterns('',
#...
url(r'whatever/(P?<subject>[a-z-]+>)/$', 'myviews.index', ...),
)
# myviews.py
def index(request, subject):
# do whatever
context = {
# whatever else
'subject':subject
}
return render(request, "whatever/index.html", context)
# whatever/index.html
<html>
# headers etc
<body class="something {{ subject }} etc">
# whatever here
</body>
</html>
You can do this is many ways.
In general you need to return some variable from your view to the html and depending on this variable select a style sheet, if your variable name will match you style sheet's name you can do "{{variable}}.css", if not you can use JQuery.

Why does this Google App Engine ndb ancestor query not work?

I've got a web app that's a simple wiki. It allows users who are logged in to edit existing topics or create new content if that topic doesn't have a page. I have been struggling with creating a version history page that lists the last n edits of a topic along with date/editor. However, I finally succeeded in displaying a list of n previous versions on the history page for a topic. My question is not exactly why what I'm doing now works... but why what I was trying before wasn't working.
Here is the handler class for a 'history' page. It's get method receives the topic as an arg in the form '/topic':
class History(Main):
""" Display previous versions of a topic if they exist """
def get(self, topic):
topic = topic[1:]
history = Version.get_topic_history(topic).fetch(10)
if history:
self.render('history.html', history=history, topic=topic)
else:
self.redirect('/%s' % topic)
Here is the model for storing all topic edits. It has a classmethod, get_topic_history. It queries the Version class for all entities by topic name, and then returns them ordered by creation date. This works. However, you can see just above that line, commented out, is what I was doing originally, that didn't work. In the first commented out line, I retrieved the key for all entities with the ancestor path that included the particular topic name(I think this is called an ancestor query, and at least, this is what I understand it to be doing). Then I returned a query by ancestor path, and ordered also by creation date/time.
This method is called from the History handler as you can see. This to me, looks like it should have returned the same result as my current approach, yet it returned nothing. Can anyone tell me why? And thanks in advance for all answers.
class Version(ndb.Model):
""" wiki version history """
created = ndb.DateTimeProperty(auto_now_add=True)
author = ndb.StringProperty(required=True)
topic = ndb.StringProperty(required=True)
content = ndb.TextProperty(required=True)
#classmethod
def get_topic_history(cls, topic):
# key = ndb.Key(cls, topic, parent=version_key())
# return cls.query(ancestor=key).order(-cls.created)
return cls.query(cls.topic == topic).order(-cls.created)
this is how I store the versions:
version = Version(topic=topic,
content=content,
author=User.get_user_name(self.user_id()),
parent=version_key())
version.put()
The version key function assigning parent keys in the code above is outside any class:
def version_key():
return ndb.Key('Group', 'version_group')
Finally here is a history.html template example. I am using Jinja2:
{% extends "base.html" %}
{% block content %}
Topic edit history :: {{topic}}
<div>
{% for version in history %}
<div>
{{version.content}}<br>
{{version.created}} - {{version.author}}
view
</div>
{% endfor %}
</div>
{% endblock %}
When you create the Version entity, the parent is version_key().
When you issue the ancestor query, the ancestor should be version_key() as well. However, you are querying with the ancestor ndb.Key(cls, topic, parent=version_key())
I believe what you want to do is:
cls.query(ancestor=version_key()).filter(topic=topic).order(-cls.created)
btw, having a single ancestor is going to be bad for your future performance of creating Version entities.

how to extend django views/templates in a forward looking way

i'm looking for a way to extend a django template/view.
my first implementation consists of two models (clients/models.py):
class Client(models.Model):
...
class Address(models.Model):
client = models.ForeignKey(Client)
...
and its fairly simple template (clients/detail.html) :
{{client.name}}
Address: {{client.address.street}}, {{client.address.zipcode}} {{client.address.city}}
as my application grows, a new app was born: 'invoices'.
it is again very simple (invoices/models.py):
class Invoice(models.Model):
client = models.ForeignKey(clients.models.Client)
...
now my clients details-view needs to display invoices, so i create and override clients/detail.html in my 'invoices' app.
good for now.
later on i created a third app 'quotes'.
again my clients details-view needs to display quotes.
if i create clients/detail.html in my 'clients' i will loose the ability to display invoices.
because the 'invoices' and 'quotes' app are indipendent.
my first idea was to create something like a SubView-class
which 'invoices' and 'quotes' can extend and then register their implementation somewhere.
a template should look like this:
{{client.name}}
Address: {{client.address.street}}, {{client.address.zipcode}} {{client.address.city}}
{% for view in views %}
<h1>{{view.title}}</h1>
{{view.get_html}}
{% endfor %}
is this a good way to go and should i use a admin.site-like implementation for registering my sub-views?
In Django one url in urls.py should ideally use one view, just to keep things simple.
I would therefore adopt the approach of putting all the required context in your one view for this screen (I think you already have this via foreign keys in your model). Then, rather than doing what you call "SubView-class" I would go for the Django template include tag.
Example:
{% for invoice in client.invoices %}
{% include "invoice-detail.html" with invoice=invoice %}
{% endfor %}
This renders each invoice's detail for all the invoices of the client. Notice how this is in line with the DRY principle.

Django History for Custom Dashboard

I have decided to use Django-Simple-History for building a history of my models. In turn using that to build the dashboard. I have run into a bit of a snag though. I'm trying to output [User] [added,changed, deleted] [object] on/at [time] but I can't figure it out for the life of me.
So far I am able to display the historical record on the template but I can't access anything else, am I missing something?
I was hoping someone with knowledge of Simple History can help, since I couldn't get a hold of the author.
Here is the code snippets I have so far.
Models.py
from simple_history.models import HistoricalRecords
class Project(django.db.models.Model):
...
history = HistoricalRecords()
Views.py
#login_required
def addTMProject(request):
user = request.user
if request.method == 'POST':
form = TimeMaterialsForm(request.POST)
if form.is_valid():
project = form.save(commit=False)
project.created_by = request.user
today = datetime.date.today()
project.pre_quote = "%s-" % (str(today.year)[2:4])
project.quote = Project.objects.latest().quote+1
project.save()
project.history.all()
...
And I have also passed it on my dashboard/views.py so I have access to it.
#login_required
def view_dash(request):
today = datetime.date.today()
user = request.user
proj_perm = user.has_perm('project.add_project')
project = Project.objects.all().order_by('-proj_name')
query = Project.objects.all().order_by('-id')[:5]
que_quotes = Project.objects.filter(status__value__exact = 'Quote')
expired = FollowUp.objects.filter(next_followup__lte=today).order_by('next_followup').filter(archived=False)
log = LogEntry.objects.select_related().all().order_by("-id")
hist = Project.history.all()
return render_to_response('dashboard/home.html', {'user': user, 'project': project, 'query':query, 'que_quotes':que_quotes, 'expired':expired,
'proj_perm':proj_perm, 'log': log, 'hist':hist,}, context_instance=RequestContext(request))
And finally a snippet from my template. As it is right now, the {{ h }} shows "Testing Simple Records as of 2011-04-29 10:43:57" on the template
home.html
{% if user.is_authenticated %}
<div id="large_box">
<h5>Activity</h5>
{% for h in hist %}
<ul>
<li>{{ h }}</li>
</ul>
{% endfor %}
If anyone could help or point me to some more in depth documentation, then that would be great!
Thanks everyone!
Django-Simple-History simply creates a model (and associated database table) that mirrors the object you tie it to and adds four additional fields: history_id, history_date, history_type, and history_object.
history_id: standard primary key
history_date: datetime for when the change occurred
history_type: one of +, ~, -. (+ means added, ~ means changed, and - means deleted)
history_object: representation of the model that history is being stored for
So, at most basic level, you can roughly get "[added,changed, deleted] [object] on/at [time]" in your output, using something to the effect of:
{{ h.history_type }} {{ h.history_object }} on/at {{ h.history_date }}
You'll probably want to create template tag or something to convert the +, ~, and - to the more understandable, 'Created', 'Changed', 'Deleted'. {{ h.history_object }} should return the object's __unicode__, I'm assuming, so you might need to make some modifications there or return something like {{ h.history_object.__class__ }} or {{ h.history_object._meta.verbose_name }}, instead. (Not sure if those will actually work in practice though.) And, of course, you can apply the date filter to {{ h.history_date }} to make it any format you want.
Getting the user is more difficult. Django-Simple-History doesn't seem to store this data, so there's no record of what user made the modification. However, since it basically duplicates the object as it existed, you could probably get away with adding a modified_by field to your model and filling that with request.user pre-save. Then, when Django-Simple-History does its thing, that field would be copied over like the rest and be available via {{ h.modified_by }}.
I'm assuming that the only problem you're having is with the displaying of the historical data and not the actual saving portion of it.
I'm not sure what fields you have in your Project model, but it looks like the history field is treated like a foreign key field. This foreign key's table contains the same fields that your Project model does. So, if you want to access the fields you'd have to do something like this in your template:
...
{% for h in hist %}
<ul>
<li>{{h.field1}} {{h.field2}} {{h.field3}} on {{h.field4}}</li>
</ul>
{% endfor %}
...
I found this page (http://qr7.com/2010/10/django-simple-history-ftw/) which was quite helpful, but you'll have to play around with the history field names. I wasn't quite sure what they actually were.
Hope that helps.

Categories

Resources