App引擎模型,get_or_insert问题w.r.t主键和组合键 [英] App engine model, get_or_insert question w.r.t primary key and composite keys
问题描述
我在模型之间有1到多个层次关系
国家(1) - >城市(很多)
城市(1) - >状态(很多)
所以,只能有一个独特的国家,一个国家只能有一个独特的城市,而一个城市可能有很多状态。我打算使用get_or_insert方法以确保我在数据库中保留唯一的记录。
B]代码摘录:
$ b $模型结构 -
class UserReportedCountry(db.Model):
name = db.StringProperty(required = True)
$ b $ class UserReportedCity(db.Model):
country = db.ReferenceProperty(UserReportedCountry,collection_name ='cities')
name = db.StringProperty(required = True)
$ b $ class UserReportedStatus(db.Model):
city = db.ReferenceProperty(UserReportedCity,collection_name ='status')
status = db.BooleanProperty()
date_time = db.DateTimeProperty(auto_now_add = True)
2]用于存储从HTML表单中检索到的数据的代码:
$ b
def store_user_data self):
country_name = self.request.get('selCountry')
user_reported_country = UserReportedCountry.get_or_insert(name = country_name)
user_reported_city = UserReportedCity.get_or_insert(name = self .request.get('city'),country = user_reported_country)
user_reported_status = UserReportedStatus(status = self.request.get('status'),city = user_reported_city)
user_reported_status.put ()
问题:
1]从谷歌搜索,它似乎get_or_insert需要一个键,在我的情况下,在UserReportedCountry模型中,我希望国家的名称是主键
和在UserReportedCity模型,即i希望国名+城市名称的组合成为关键。我怎么去做这件事?
2]有没有一种方法可以在不指定键的情况下使用get_or_insert,我在下面的文章中提到了stackoverflow(http: //stackoverflow.com/questions/4308002/google-app-engine-datastore-get-or-insert-key-name-confusion),并尝试了这个主意,但它没有奏效。
感谢您的阅读,
基于@Josh Smeaton给出的响应所做的更改总结
现在代码检查用户报告的国家是否存在于数据库中。如果用户报告的国家/地区不存在,那么代码会创建一个UserReportedCountry,UserReportedCity并为其添加新的状态。
2]如果国家存在,则代码检查用户报告的城市是否存在于指定的国家。
如果未找到该城市,则创建城市记录并将其与所找到的国家相关联,状态记录。
如果找到了该城市,请附上状态记录。
要求:
我会非常感谢,如果有人可以请做一个代码审查,并让我知道我是否犯了错误。
感谢,
代码摘录:
#此方法将用于解析用户在html表单中提供的数据,并将其存储在数据库模型
#中,同时维护UserReportedCountry,UserReportedCity和UserReportedStatus
#BUG之间的关系,需要错误检查以确保国家,城市和状态数据无效
#如果数据无效,则需要报告错误消息,然后重定向回主页
def store_user_data( self):
#method调用来查找完整填写的UserReportedCity模型
user_reported_city = self.find_or_create_user_reported_country_and_city(
self.request.get('selCountry'),self.request.get('city'))
#status对于用户条目而言始终是唯一的,因此每次都要创建一个全新的UserReportedStatus。
user_reported_status = UserReportedStatus(status = self.get_user_reported_status(),city = user_reported_city)
user_reported_status.put()
#代码需要找出是否存在用户选择的国家/城市
#1]如果用户报告的国家不存在,则创建一个新的国家记录,创建一个新的城市记录并返回城市记录
#2]如果用户报告的国家存在,请检查用户报告的城市是否与该国家相关联。
#如果城市存在,则返回。如果城市不存在,则创建一个新城市并返回它
#example:如果用户选择美国,则需要检查美国是否已存在或不存在
#so我们不存在创建一个额外的美国记录
def find_or_create_user_reported_country_and_city(self,country_name,city_name):
country_query_result = db.GqlQuery(SELECT * FROM UserReportedCountry WHERE name =:country_name_value
,country_name_value = country_name)。 get()
if(country_query_result == None):
#since国家不存在,创建并保存国家
user_reported_country = self.create_and_save_user_country_record(country_name)
#由于该国不存在,因此不能为该国提供城市记录,因此一味创建记录
return self.create_and_save_user_city_record(city_name,user_reported_country)
else:
#自从我们找到了一个国家尝试,现在我们需要找到用户选择的城市是否存在于给定的国家
返回self.find_or_create_city_for_country(country_query_result,city_name)
#检查用户选择的城市是否存在于该国
#1]如果城市存在则返回记录
#2]如果城市不存在creaty城市记录并返回它
def find_or_create_city_for_country(self,country_record,city_name):
city_query_result = db.GqlQuery(SELECT * FROM UserReportedCity WHERE name =:city_name_value AND country =:country_value
,city_name_value = city_name,country_value = country_record).get()
if city_query_result == None):
#由于城市不存在于给定的国家,
#创建城市记录,将其与国家相关联并返回记录
return self.create_and_save_user_city_record( city_name,country_record)
else:
#since发现城市,返回记录返回
返回city_query_result
#method为给定国家/地区名称创建UserReportedCountry记录
def create_and_save_user_country_record(self,country_name ):
user_reported_country = UserReportedCountry(name = country_name)
user_reported_country.put()
返回user_reported_country
#为给定城市名称创建UserReportedCity记录的方法,以及给定的国家记录
def create_and_save_user_city_record(self,city_name,country_record):
user_reported_city = UserReportedCity(name = city_name,country = country_record)
user_reported_city.put()
return user_reported_city
html表单,保存数据的调用是使用post完成的。您认为这仍然是一个问题吗?
< div id =userDataForm>
< form method =postaction =/ UserReporting>
< p>选择国家:< / p>
< select name =selCountryid =country>
<! - 默认情况下,我们将选择用户国家 - >
< script type =text / javascriptlanguage =JavaScript>
document.write(< option value = \+ geoip_country_name()+\selected>
< / script>
:
:
:
选择城市:< / p>
< div>
< input type =textname =cityid =city >
<! - 默认情况下,我们将选择用户城市 - >
< script type =text / javascriptlanguage =JavaScript>
document.getElementById(city)。value = geoip_city()
< / script>
< / div>
<输入类型=submitname =report_downvalue =Report Down>
< / form>< input type =submitname =report_upvalue =Report Up ;
< div>
最初我尝试过使用Djangoforms,我不知道如何使用javascript在djangoform中选择一个值
1]从google搜索中,它显示
get_or_insert需要一个键,在UserReportedCountry
模型的
情况下,我希望国家
的名称是主键,并且在
UserReportedCity 模式,我希望国家名称+城市
名称的
组合是关键。我该如何去做
这样做?
只需指定国家的名称以及国家和城市的联合(例如美国/旧金山作为您传递给 get_or_insert
的关键名称。另外, get_or_insert
语法糖如下:
$ b $ pre $ def $ get_or_insert(cls,key_name,** kwargs):
def _tx() :
obj = cls.get_by_key_name(key_name)
如果obj是None:
return cls(key_name,** kwargs)
else:
return obj
返回db.run_in_transaction(_tx)
2]有没有办法要使用
get_or_insert而不指定
键,我在以下
发布了stackoverflow
(http://stackoverflow.com/questions/4308002/google-app -bengine-datastore-get-or-insert-key-name-confusion),
并尝试了这个主意,但它没有奏效。 b它doe这样做真的没有意义。关键是App Engine中模型的唯一唯一字段,并且您不能在App Engine中执行跨实体组查询,因此除非指定了一个,否则无法执行事务性的获取或插入操作。根据您的要求,使用国家名称和城市名称作为关键名称应该可以正常工作。
A] Summary of the problem:
I Have 1 to many hierarchical relationships between models
Country (1) --> City (Many)
City (1) --> Status (Many)So, there can only be one unique country, a country can only have one unique city, and a city could have many statuses
I am planning to use "get_or_insert" method to make sure that i maintain unique records in the database.
B] Code Excerpts:
1] Model structure --
class UserReportedCountry(db.Model): name = db.StringProperty(required=True) class UserReportedCity(db.Model): country = db.ReferenceProperty(UserReportedCountry, collection_name='cities') name = db.StringProperty(required=True) class UserReportedStatus(db.Model): city = db.ReferenceProperty(UserReportedCity, collection_name='statuses') status = db.BooleanProperty() date_time = db.DateTimeProperty(auto_now_add=True)
2] Code that is used for storing the data retrieved from the HTML form:
def store_user_data(self): country_name = self.request.get('selCountry') user_reported_country = UserReportedCountry.get_or_insert(name=country_name) user_reported_city = UserReportedCity.get_or_insert( name = self.request.get('city'), country = user_reported_country ) user_reported_status = UserReportedStatus( status = self.request.get('status'), city = user_reported_city) user_reported_status.put()
Questions:
1] From the google search, it appears "get_or_insert" requires a key, In my case in the "UserReportedCountry" model, i want the name of the country to be the primary key and in the "UserReportedCity" model, i want the combination of country name + city name to be the key. How do i go about doing this ?
2] Is there a way to use "get_or_insert" without specifying a key, I came accross the following posting on stackoverflow (http://stackoverflow.com/questions/4308002/google-app-engine-datastore-get-or-insert-key-name-confusion), and tried the idea but it didnt work.
Thank you for reading,
[EDIT#1]
Summary of the changes based on the response given by @Josh Smeaton
1] Now the code checks if the user reported country is present in the db or not. If the user reported country is not present, then the code creates a UserReportedCountry, UserReportedCity and attaches a new status to it
2] If the country is present, then the code checks whether the user reported city is present for the given country.
If the city is not found, then create a city record and associated it with the found country and attach a status record.
If the city is found, then attach the status record to it.
Request:
I will highly appreciate, If someone can please do a code review and let me know if i am making any mistakes.
Thanks,
Code excerpts:
#this method will be used to parse the data the user provided in the html form and store it in the database models #while maintaing the relationship between UserReportedCountry, UserReportedCity and UserReportedStatus #BUG, there needs to be error checking to make sure the country , city and status data is invalid or not #if the data is invalid, then error message needs to be reported and then redirection back to the main page def store_user_data(self): #method call to find out the completly filled out UserReportedCity model user_reported_city = self.find_or_create_user_reported_country_and_city( self.request.get('selCountry'), self.request.get('city')) #status is always unique for a user entry, so create a brand new UserReportedStatus everytime. user_reported_status = UserReportedStatus(status = self.get_user_reported_status(), city = user_reported_city) user_reported_status.put() #Here the code needs to find out if there is an existing country/city for the user selection #1] If the user reported country doesnt exist, create a new country record, create a new city record and return the city record #2] If the user reported country exists, check if the user reported city is associated with the country. #if the city exists, then return it. If the city doesnt exists, then create a new city and return it #example: if the user chooses USA, there needs to be a check if USA is already present or not, #so that we dont create an additonal USA record def find_or_create_user_reported_country_and_city(self, country_name, city_name): country_query_result = db.GqlQuery("SELECT * FROM UserReportedCountry WHERE name = :country_name_value" ,country_name_value = country_name).get() if (country_query_result == None): #since the country doesnt exists, create and save the country user_reported_country = self.create_and_save_user_country_record(country_name) #Since the country doesnt exist, there cannot be a city record for the given country, so blindly create the record return self.create_and_save_user_city_record(city_name, user_reported_country) else: #Since we found a country, now we need to find whether the user selected city exists for the given country return self.find_or_create_city_for_country(country_query_result, city_name) #Check wheter the user selectred city exists in the country #1] if the city exists return the record back #2] if the city doesnt exist creaty the city record and return it def find_or_create_city_for_country(self, country_record, city_name): city_query_result = db.GqlQuery("SELECT * FROM UserReportedCity WHERE name = :city_name_value AND country =:country_value" ,city_name_value = city_name, country_value = country_record ).get() if (city_query_result == None): #Since the city doesnt exist for the given country, #create the city record, associated it with the country and return the record back return self.create_and_save_user_city_record(city_name, country_record) else: #since the city was found, return the record back return city_query_result #method to create a UserReportedCountry record for a given country name def create_and_save_user_country_record(self, country_name): user_reported_country = UserReportedCountry(name= country_name) user_reported_country.put() return user_reported_country #method to create a UserReportedCity record for a given city name and a given country record def create_and_save_user_city_record (self, city_name, country_record): user_reported_city = UserReportedCity(name = city_name, country = country_record) user_reported_city.put() return user_reported_city
[EDIT#2]
Inside the html form, the call to save the data is done using "post". Do you think this is still a problem?
<div id="userDataForm"> <form method="post" action="/UserReporting"> <p> Select Country: </p> <select name="selCountry" id="country"> <!-- By default, we will select users country --> <script type="text/javascript" language="JavaScript"> document.write("<option value=\"" + geoip_country_name() + "\" selected>" </script> : : : <p> Select City: </p> <div> <input type="text" name="city" id="city"> <!-- By default, we will select users city --> <script type="text/javascript" language="JavaScript"> document.getElementById("city").value = geoip_city() </script> </div> <input type="submit" name="report_down" value="Report Down"> <input type="submit" name="report_up" value="Report Up"> </form> <div>
Initially i tried using the Djangoforms, but i got blocked because i didnt knew how to use javascript to select a value in the djangoform
解决方案To address your questions in order:
1] From the google search, it appears "get_or_insert" requires a key, In my case in the "UserReportedCountry" model, i want the name of the country to be the primary key and in the "UserReportedCity" model, i want the combination of country name + city name to be the key. How do i go about doing this ?
Simply specify the name of the country, and the concatenation of country and city (eg "USA/San Francisco" as the key names you pass to
get_or_insert
. As an aside,get_or_insert
is simply syntactic sugar for the following:def get_or_insert(cls, key_name, **kwargs): def _tx(): obj = cls.get_by_key_name(key_name) if obj is None: return cls(key_name, **kwargs) else: return obj return db.run_in_transaction(_tx)
2] Is there a way to use "get_or_insert" without specifying a key, I came accross the following posting on stackoverflow (http://stackoverflow.com/questions/4308002/google-app-engine-datastore-get-or-insert-key-name-confusion), and tried the idea but it didnt work.
It doesn't really make sense to do so. The key is the only unique field for a model in App Engine, and you can't do cross-entity-group queries in App Engine, so unless you specify one it's not possible to do a transactional get-or-insert operation. Given your requirements, though, using country name and city name as key names ought to work just fine.
这篇关于App引擎模型,get_or_insert问题w.r.t主键和组合键的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!