Google App Engine, How to automatically load model class for ReferenceProperty - google-app-engine

I have modular project structure, like this:
./main.py
./app.yaml
../articles
.../__init__.py
.../models.py
../blog
.../__init__.py
.../models.py
../comments
.../__init__.py
.../models.py
I have defined models in file models.py for each package (this is application). I have defined next models for "comments" application:
class Comment(db.Model):
author = db.UserProperty(auto_current_user_add=True)
title = db.StringProperty(default="Title")
text = db.TextProperty("Message", default="Your message")
# references to any model
object = db.ReferenceProperty()
and in "articles" application I have defined next models:
class Article(db.Model):
author = db.UserProperty(auto_current_user_add=True)
title = db.StringProperty(default="Title")
text = db.TextProperty("Message", default="Your message")
1) On first loading of page - I create new article:
from articles.models import Article
article = Article(title="First article", text="This is first article")
article.put()
2) On second loading of page I create new comment:
from articles.models import Article
from comments.models import Comment
article = Article.get_by_id(1)
comment = Comment(title="First comment", text="This is first comment")
comment.put()
3) On thind loading of page, I want to see all comments for all articles, blogs, and other objects in whole datastore:
from comments.models import Comment
comments = Commen.all()
for comment in comments:
# print comment and article title
print "%s: %s" % (comment.title, comment.object.title)
Actual result: "KindError: No implementation for kind 'Article'"
Expected result: automatically detect object type for reference and load this class
See more on: http://code.google.com/p/appengine-framework/issues/detail?id=17
Project need your help!

In order to be able to return entities of a given kind, App Engine has to be able to find the Model class for it. There's no mechanism built in for doing so, because all it has to look it up with is the entity kind, which can be any arbitrary string.
Instead, import the modules containing the models you may reference from the module containing the Comment model. That way, any time you can perform a query on Comment, all the relevant models are already loaded.

In my project GAE framework I have solve this issue. On the first page loading I have load all models in memory.
What if we have models with the same name, for example Comment model in "blog" and "board" applications? In this case we have automatically add prefix for models for the King of this model. In result we have the different names for models in different applications: BlogComment and BoardComment.
You can learn more in source code to understand how we do this implementation.

Related

Programatically add a page to a known parent

I would like to create programatically a sub page for a known parent. How can I do that? The page creation will takes place in a signal receiver: the page is created on publication of another page.
Add a revision as well.
from wagtail.wagtailcore.models import Page
from models import MyPage
home = Page.objects.get(id=3) # or better Page query
my_page = MyPage(title="test", body="<h1>the body</h1>")
home.add_child(instance=my_page)
# later when a cms user updates the page manually
# there will be no first revision to compare against unless
# you add a page revision also programmatically.
my_page.save_revision().publish()
You can see how wagtail does this in the wagtailadmin pages create view (line 156).
https://github.com/wagtail/wagtail/blob/stable/1.13.x/wagtail/wagtailadmin/views/pages.py
Update 2018-09-18:
I built a 700 page site including 200 generated pages. I never added an initial Revision anywhere and no editors complained. After the first manual edit there will be a Revision. Go ahead and add an initial Revision if you think it is needed for traceability.
To create a page programmatically:
page = SomePageType(title="My new page", body="<p>Hello world</p>") # adjust fields to match your page type
parent_page.add_child(instance=page)
Below is my complete code to create a multi language page structure programatically. It will replace the "Wagtail Welcome Page" with a LanguageRedirectionPage instance.
More information about multi language pages:
Wagtail Docs - Internationalization
The page structure is as follows:
Page
LanguageRedirectionPage (will redirect to /en)
Page (en)
Page (de)
Page (fr)
where the created Site instance at the end of the code points to the LanguageRedirectionPage instance. This is the entry point of our application.
# Deletes existing pages and sites
Site.objects.all().delete()
Page.objects.filter(pk=2).delete() # Deletes Wagtail welcome page
root_page = Page.objects.filter(pk=1).get()
# Adds a LanguageRedirectionPage as a child of the Root Page
app_name = '[Your Project Name]'
page_slug = app_name.lower().replace(" ", "")
sub_root_page = LanguageRedirectionPage(
title=app_name,
draft_title=app_name,
slug=page_slug,
live=True,
owner=account,
)
root_page.add_child(instance=sub_root_page)
sub_root_page.save_revision().publish()
# Adds some language pages
for code,caption in dict(settings.LANGUAGES).items():
print(code, caption)
sub_root_page.add_child(instance=Page(
title=caption,
slug=code,
live=True,
owner=account,
))
# Adds a new Site instance (See Settings -> Sites in your Wagtail admin panel)
Site.objects.create(
hostname='localhost',
port='80',
site_name=app_name,
root_page=sub_root_page,
is_default_site=True,
)

How do you check in code if a request matches an EPiServer Visitor Group

We've set up a new "visitor group" in EPiServer 6r2, and we want to add a css class to the <body> tag of the site if the user is in that group, so different groups get different site designs. I'm trying to find out if the current visitor is in a matching group in the code-behind of a masterpage file in order to add this extra class and can't get the below code to return anything but false.
I'm not sure if the role name mentioned is the name you enter in the CMS UI when adding a visitor group.
Paul Smith blogged a proposed solution to this but I haven't been able to get it to return anything but false yet, and judging by the only comment on the blog article I'm not alone. Code sample #1 from this link (which is the one I'm using):
using EPiServer.Personalization.VisitorGroups;
...
bool match = EPiServer.Security.PrincipalInfo.CurrentPrincipal
.IsInRole("My Visitor Group", SecurityEntityType.VisitorGroup);
I found the developer guide to membership and role providers which states that replacePrincipal must be set to true for the correct principal to be in place. I checked and this is already the case for my config.
Documentation
EPiServer 7 doc
IPrincipal.IsInRole() extension
SecurityEntityType enum
Oddly I searched the 6r2 documentation from http://sdk.episerver.com/ and can't find the documentation for IPrincipalExtensions at all, even though I see the class in object browser in 6.2. in my sln. Details: Assembly EPiServer.ApplicationModules - C:\Windows\assembly\GAC_MSIL\EPiServer.ApplicationModules\6.2.267.1__8fe83dea738b45b7\EPiServer.ApplicationModules.dll - public static bool IsInRole(this System.Security.Principal.IPrincipal principal, string role, EPiServer.Security.SecurityEntityType type)
Member of EPiServer.Personalization.VisitorGroups.IPrinicipalExtensions
Please comment if you spot errors or I've missed anything as coding for EPiServer is a bit of a fog-of-war affair and I'm a little battle-weary.
Found it by browsing the object model and guessing. So much for documentation.
using EPiServer.Personalization.VisitorGroups;
using EPiServer.Security;
const string visitorGroupName = "Some users";
var groupHelper = new VisitorGroupHelper();
bool isPrincipalInGroup = groupHelper.IsPrincipalInGroup(
PrincipalInfo.CurrentPrincipal, visitorGroupName);
Tested and working in EPiServer 6r2 (aka 6.1).
String visitorGroupName must match the string entered into the "Name" box on the EPiServer admin interface when creating / editing the visitor group. See screenshot below:

google-app-engine: querying a String property in Model doesn't retrieves result

I have a Model represented as
class Suggestion(db.Model):
text = db.TextProperty()
votes = db.IntegerProperty()
time_added = db.DateTimeProperty(auto_now_add=True)
time_modified = db.DateTimeProperty(auto_now=True)
and I added a Suggestion as
suggestion = Suggestion(text='Adding Suggestion', votes=1)
suggestion.put()
I see that that the value is inserted, now I want to get this suggestion by querying the text property. I did the following
from models import Suggestion
suggestion = Suggestion.all().filter('text = ', 'Adding Suggestion').fetch(1)[0]
print suggestion
The result is empty. How can I make this query work?
Thank you
It is due to the text property cannot be used in filters.
http://code.google.com/appengine/docs/python/datastore/typesandpropertyclasses.html#TextProperty
Unlike StringProperty, a TextProperty value can be more than 500
characters long. However, TextProperty values are not indexed, and
cannot be used in filters or sort orders
There are some alternative ways to do so.
If the "text" shorter than 500 characters, then you may use StringProperty. StringProperty can be used in filter.
Try the 3rd party full text search approaches for google app engine.
http://code.google.com/p/guestbook-example-appengine-full-text-search/
App engine team is working on providing The full text search function in near future.
http://googleappengine.blogspot.com/2012/01/happy-birthday-high-replication.html
Edit
the full text functin is developed by google app engine team:
https://developers.google.com/appengine/docs/python/search/overview

What approach is best for mapping a legacy application tables named after years in Django?

Better see what the table names look like:
2009_articles
2010_articles
2011_articles
2009_customers
2010_customers
2011_customers
2009_invoices
2010_invoices
2011_invoices
Developers have simulated some kind of partitioning (long before mysql supported it) but now it breaks any try to make a quick frontend so customers can see their invoices and switch years.
After a couple on months I have the following results:
Changing Invoice._meta.db_table is useless cause any other relation deduced by the ORM will be wrong
models.py cannot get request variables
Option a:
Use abstract models so Invoice10 adds meta.db_table=2010 and inherits from Invoice model, and Invoice11 adds meta.db_table=2011, Not DRY although the app shouldn't need to support more than two or three years at the same time, but I will have to still check if
Option b:
Duplicate models and change imports on my views:
if year == 2010:
from models import Article10 as Article
and so on
Option c:
Dynamic models as referred to in several places on the net, but why have a 100% dynamic model when I just need a 1% part of the model dynamic?
Option d:
Wow, just going crazy after frustration. What about multiple database settings and use a router?
Any help will be much appreciated.
Option e: create new relevant models/database structure, and do an import of the old data in the new structure.
Ugly, but must inform.
I achieved something useful by using Django signal: class_prepared and ThreadLocal to get session:
from django.db.models.signals import class_prepared
from myapp.middlewares import ThreadLocal
apps = ('oneapp', 'otherapp',)
def add_table_prefix(sender, *args, **kwargs):
if sender._meta.app_label in apps:
request = ThreadLocal.get_current_request()
try:
year = request.session['current_year']
except:
year = settings.CURRENT_YEAR
prefix = getattr(settings, 'TABLE_PREFIX', '')
sender._meta.db_table = ('%s_%s_%s_%s') % (prefix, year, sender._meta.coolgest_prefix, sender._meta.db_table)
class_prepared.connect(add_table_prefix)
So is one model class mapping several identical database tables (invoices_01_2013, invoices_02_2013, ...) depending of what month and year the application user is browsing.
Working fine in production.

How do I dynamically determine if a Model class exist in Google App Engine?

I want to be able to take a dynamically created string, say "Pigeon" and determine at runtime whether Google App Engine has a Model class defined in this project named "Pigeon". If "Pigeon" is the name of a existant model class, I would like to then get a reference to the Pigeon class so defined.
Also, I don't want to use eval at all, since the dynamic string "Pigeon" in this case, comes from outside.
You could try, although probably very, very bad practice:
def get_class_instance(nm) :
try :
return eval(nm+'()')
except :
return None
Also, to make that safer, you could give eval a locals hash: eval(nm+'()', {'Pigeon':pigeon})
I'm not sure if that would work, and it definitely has an issue: if there is a function called the value of nm, it would return that:
def Pigeon() :
return "Pigeon"
print(get_class_instance('Pigeon')) # >> 'Pigeon'
EDIT: Another way of doing it is possibly (untested), if you know the module:
(Sorry, I keep forgetting it's not obj.hasattr, its hasattr(obj)!)
import models as m
def get_class_instance(nm) :
if hasattr(m, nm) :
return getattr(m, nm)()
else : return None
EDIT 2: Yes, it does work! Woo!
Actually, looking through the source code and interweb, I found a undocumented method that seems to fit the bill.
from google.appengine.ext import db
key = "ModelObject" #This is a dynamically generated string
klass = db.class_for_kind(key)
This method will throw a descriptive exception if the class does not exist, so you should probably catch it if the key string comes from the outside.
There's two fairly easy ways to do this without relying on internal details:
Use the google.appengine.api.datastore API, like so:
from google.appengine.api import datastore
q = datastore.Query('EntityType')
if q.get(1):
print "EntityType exists!"
The other option is to use the db.Expando class:
def GetEntityClass(entity_type):
class Entity(db.Expando):
#classmethod
def kind(cls):
return entity_type
return Entity
cls = GetEntityClass('EntityType')
if cls.all().get():
print "EntityType exists!"
The latter has the advantage that you can use GetEntityClass to generate an Expando class for any entity type, and interact with it the same way you would a normal class.

Resources