2011-03-26 37 views
1

A]這個問題的總結:應用程序引擎模型,get_or_insert問題WRT主鍵和組合鍵

我有1款

國家(1)之間有許多層次關係 - >城市(許多)
市(1) - >狀態(很多)

所以,只能有一個獨特的國家,一個國家只能有一個獨特的城市,和一個城市可以有很多狀態

我打算使用「get_or_insert」方法來確保我維護數據庫中的唯一記錄。

B]代碼摘錄:

1]模擬結構 -

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]的代碼,用於存儲從HTML表單中檢索到的數據:

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

Q uestions:

1]從谷歌搜索,似乎「get_or_insert」要求的關鍵,在我的情況下,在「UserReportedCountry」的模式,我希望國家的名稱是主鍵 並在「 UserReportedCity「模式,我希望國家名稱+城市名稱的組合是關鍵。我如何去做這件事?

2]有沒有一種方法來使用「get_or_insert」沒有指定一個鍵,我來到以下貼在stackoverflow(http://stackoverflow.com/questions/4308002/google-app-engine-datastore-get - 或 - 插入 - 鍵名稱 - 混淆),並嘗試了這個想法,但它沒有奏效。

謝謝您的閱讀,

[EDIT#1]

基於由@Josh斯米頓

1]現在的代碼檢查是否用戶給出的響應的變化概要報告的國家是否存在於數據庫中。如果用戶報告的國家不存在,那麼代碼將創建一個UserReportedCountry,UserReportedCity併爲其添加一個新狀態。

2]如果國家存在,則代碼檢查用戶報告的城市是否存在國家。

如果未找到該城市,則創建城市記錄並將其與所找到的國家相關聯並附上狀態記錄。

如果找到該城市,則將狀態記錄附加到該城市。

請求:

我將高度讚賞,如果有人能請做一個代碼審查,讓我知道,如果我犯錯誤。

感謝,

代碼摘錄:

#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 

[編輯#2]

裏面的html表單,保存數據呼叫使用 「後」 來完成。你認爲這仍然是一個問題嗎?

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

起初我嘗試使用Djangoforms,但我得到了阻止,因爲我不知道如何使用JavaScript在djangoform

+1

你似乎違反了HTTP的基本規則之一。 GET應該永遠不要執行破壞性操作(INSERT/DELETE/UPDATE)。應該使用POST HTTP方法。您應該使用表單,並將所有這些實現細節包含在您創建的表單中。我不熟悉GAE,只有Django,所以下面的文章可能會有所幫助。 HTTP://jamesgae.appspot。COM /博客/ 2010/01/08 /使用-Django的表單上的應用程序引擎 – 2011-03-27 02:36:42

+0

的原因是,一個腳本可以創建與在查詢參數亂碼打你的網站上千倍,完全搞亂了你的數據。使用表單(與CSRF保護),您可以嚴重阻礙潛在的攻擊者。 – 2011-03-27 02:38:20

+0

@Josh Smeaton,在html表單中,保存數據的調用是使用「post」完成的,請參考原始文章中的「編輯#2」代碼。你認爲代碼仍然有問題嗎? 我最初使用djangoform,但我想選擇默認用戶的國家和城市價值,但我得到了阻止,因爲我不知道如何使用JavaScript來選擇在djangoform 的值,我很新的框架,所以我的問題可能是愚蠢:( – bhavesh 2011-03-27 15:37:33

回答

3

爲了解決您的問題依次是:

1]從谷歌搜索,似乎 「get_or_insert」要求的關鍵,在我 情況下,在「UserReportedCountry」 模式,我想的名字的國家 成爲主鍵,並在 「UserReportedCity」模型,我想 組合國名+城市 名稱是關鍵。我如何去做 這樣做?

只需指定國家的名稱,以及國家和城市(如「美國/舊金山」爲你傳遞給get_or_insert鍵值名的連接。順便說一句,get_or_insert是以下簡單的語法糖:

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]有沒有辦法使用 「get_or_insert」 而不指定 鍵的方式,我就翻過以下 張貼在計算器 (http://stackoverflow.com/questions/4308002 /G oogle-app-engine-datastore-get-or-insert-key-name-confusion), ,並嘗試了這個想法,但它沒有奏效。

這樣做確實沒有意義。關鍵是App Engine中模型的唯一唯一字段,並且您不能在App Engine中執行跨實體組查詢,因此除非指定了一個,否則無法執行事務性的獲取或插入操作。根據您的要求,使用國家名稱和城市名稱作爲關鍵名稱應該工作得很好。

+0

謝謝@Nick Johnson,今晚我會試試這個方法,讓你知道結果。使用這種方法,我是否仍然需要擔心@Josh Smeaton提到的CSRF問題? – bhavesh 2011-03-28 12:12:08

+0

@lance是的,你這樣做 - 我的回答只顯示你如何以你想要的方式使用數據存儲。您仍然需要確保您安全地構建Web應用程序。 – 2011-03-28 22:36:01

+0

因此,爲了避免CSRF的唯一方法就是使用Django的形式,而不是寫我自己的形式HTML?,也沒有做得到整個代碼,並插入,而使用谷歌提供的「get_or_insert」的功能?對不起,如果我天真,我是新來的,不想走錯方向。 – bhavesh 2011-03-29 00:27:05

1

我不知道GAE利用內部元類選擇一個值,但在Django中,我將使用unique字段參數作爲國家/地區定義的國家/地區名稱,unique_together Meta元組用於城市定義中的'('國家','名稱')。這會在任何情況下強制執行完整性,因爲您碰巧忘記了get_or_insert正確的咒語。

否則,請在名稱(get操作)上進行查找,如果它尚不存在,請執行插入操作。基本上,在你自己的代碼中模擬get_or_insert。

+0

@Josh斯密頓Django的模型標籤,根據您的建議在d/b中添加條目之前手動檢查國家,城市名稱及其關係之前,我已對提交的「[編輯#1]」部分進行了更改。 – bhavesh 2011-03-26 20:54:04

+1

這不是在App Engine中工作 – 2011-03-28 04:13:38

+0

@Nick,第一個或第二個想法? – 2011-03-28 07:55:57