Different default values for Model siblings Google App Engine - python

I am using an inherited modelling schema for my site, it has every media element under one common PolyModel base with every different element by themselves like so:
class STSeasonMedia(polymodel.PolyModel):
season = db.ReferenceProperty(STSeason,collection_name='related_media')
description = db.StringProperty()
visible = db.BooleanProperty(default=True)
priority = db.IntegerProperty(default=10)
So I want the "Inheriting" Models to have some other fields but also different default values, for example:
class STVideo(STSeasonMedia):
video_id = db.StringProperty()
provider = db.StringProperty()
priority = db.IntegerProperty(default = 100)
class STThumb(STSeasonMedia):
picture = db.ReferenceProperty(STPicture,collection_name='thumbs')
url = db.StringProperty()
size = db.StringProperty()
class STNote(STSeasonMedia):
content = db.TextProperty()
visible = db.BooleanProperty(default=False)
priority = db.IntegerProperty(default = 1)
Is there a way to set this different default values, they may change afterwards but in the beginning must by those values. Any idea?

I think your best solution may be to provide an __init__ method to your derived models. It can provide a modified default value for certain properties if none was provided by the user.
For example, your STVideo class, which wants a different default priority should be able to use this:
def __init__(self, priority=100, **kwargs):
super(STVideo, self).__init__(priority=priority, **kwargs)

Related

Storing and retrieving default values for fields in a related model instance

I would like to store default values for a model instance in a related object; for example, given this code:
class Contract(models.Model):
user = models.ForeignKey(User)
product = models.ForeignKey(Product)
duration = models.IntegerField(null=True, help_text='Contract validity (days)')
template = models.ForeignKey(ContractTemplate)
class ContractTemplate(models.Model):
name = models.CharField(max_length=100)
duration = models.IntegerField(help_text='Contract validity (days)')
I would like to store objects representing different common durations like:
yearly_contract = ContractTemplate.object.create(name='yearly', duration=365)
monthly_contract = ContractTemplate.object.create(name='monthly', duration=30)
and return the default value from the linked template when the object contract does not specify the value:
contract1 = Contract.objects.create(user=foo_user, foo_product, template=monthly_contract)
# contract1.duration should return 365
contract2 = Contract.objects.create(user=foo_user, foo_product, duration=45, template=monthly_contract)
# contract2.duration should return 45
So, what is the best way to achieve something like this?
You can use a callable object as default. Which seems to be what you want:
Have a look here:
https://docs.djangoproject.com/en/3.1/ref/models/fields/#default

How can I use multiple models to point to the same collection?

I have a single collection that can represent multiple types of data:
class Taxes(db.Document):
meta = {'collection': 'taxes'}
type = db.StringField() # State, local, federal
owner = db.ReferenceField(User, unique=True)
name = db.StringField()
fiscal_year = db.IntField()
What I am wanting to do is have either a DynamicEmbeddedDocument or make this a DynamicDocument to hold different models.
For example:
class Taxes(db.Document):
...
# This is made up syntax
data = db.EmbeddedDocumentField(StateTaxes, LocalTaxes, FederalTaxes)
Or:
class Taxes(db.DynamicDocument):
...
class StateTaxes(Taxes):
state_name = db.StringField()
class LocalTaxes(Taxes):
locality_name = db.StringField()
The goal is to do this:
# Embedded Dynamic Document example
taxes = Taxes.objects(owner=current_user).all()
state_taxes = [tax.data for tax in taxes if tax.type == 'state']
state_names = [tax_data.state_name for tax_data in state_taxes]
# Dynamic Document example
taxes = Taxes.objects(owner=current_user).all()
state_taxes = [tax for tax in taxes if tax.type == 'state']
state_names = [tax.state_name for tax in state_taxes]
Notes:
I must be able to perform 1 query to get back all types**.
Models should be separate in order to allow for clean definitions.
This example is very small, there would be a growing number of Models with very different definitions**.
All Models will have 4 or 5 fields that are the same.
The dynamic data should be relatively easy to query.
**These are the main reasons I am not using separate collections
Is this possible?
You could make a base class that covers all the base attributes (fields) and methods that you need. For example:
class BaseTaxes(db.Document):
name = db.StringField()
value = db.IntegerField()
meta = {'allow_inheritance': True}
def apply_tax(self, value):
return value*(1+self.value)
With this base class you can then create different versions:
class StateTaxes(BaseTaxes):
state = db.StringField()
As such the StateTaxes class inherits both attributes of BaseTaxes and its methods (more details here). Because it inherits the BaseTaxes class, it will be saved in the same collection (BaseTaxes) and queries can reach all subclasses:
results = BaseTaxes.objects().all()
And then, to split results by subclass:
state_taxes = [item for item in results if isinstance(item,StateTaxes)]

GAE inheritance in datastore

I am trying to create something like a folder structure for saving to GAE ndb datastore.
I will be saving a top root folder (MjlistitemFolder) to the datastore.
A MjlistitemFolder can have a number of other subitems (Mjlistitem) in its items property.
Ultimately the content of a folder items will be one of these: MjlistitemFolder, MjlistitemJobGeneric, MjlistitemJobApp
This all works if I create this structure in memory.
But after put()ing it away and reloading the root folder from the datastore, I don't get the same structure back.
class Mjlistitem(ndb.Model):
pass
class MjlistitemFolder(Mjlistitem):
title = ndb.StringProperty()
items = ndb.StructuredProperty(Mjlistitem, repeated=True)
class MjlistitemJob(Mjlistitem):
pass
class MjlistitemJobGeneric(MjlistitemJob):
jobtype = ndb.IntegerProperty()
class MjlistitemJobApp(MjlistitemJob):
appleid = ndb.StringProperty()
I get these warnings:
WARNING 2014-04-04 07:14:17,506 model.py:2359] Skipping unknown structured subproperty (items.items.appleid) in repeated structured property (items of MjlistitemFolder)
WARNING 2014-04-04 07:14:17,506 model.py:2359] Skipping unknown structured subproperty (items.items.jobtype) in repeated structured property (items of MjlistitemFolder)
WARNING 2014-04-04 07:14:17,506 model.py:2359] Skipping unknown structured subproperty (items.items.appleid) in repeated structured property (items of MjlistitemFolder)
It seems like the db→instance process renders the stuff in items to be of Mjlistitem class only. How do I make them appear as their real inherited classes?
This is how I create a test structure:
rootfolder = MjlistitemFolder(title="root")
subfolder = MjlistitemFolder(title="Cool things")
subfolder.items.append(MjlistitemJobApp(appleid="281796108")) # evernote
subfolder.items.append(MjlistitemJobGeneric(jobtype=3)) # phone number
subfolder.items.append(MjlistitemJobApp(appleid="327630330")) # dropbox
rootfolder.items.append(MjlistitemJobGeneric(jobtype=15)) # passbook
rootfolder.items.append(subfolder)
rootfolder.items.append(MjlistitemJobGeneric(jobtype=17)) # appstore
rootfolder.put()
use Polymodel with repeated KeyProperty
The StructuredProperty need to be changed to KeyProperty because:
TypeError: This StructuredProperty cannot use repeated=True because its model class (Mjlistitem) contains repeated properties (directly or indirectly).
The Model
from google.appengine.ext import ndb
from google.appengine.ext.ndb import polymodel
class Mjlistitem(polymodel.PolyModel):
pass
class MjlistitemFolder(Mjlistitem):
title = ndb.StringProperty()
# StructuredProperty won't allow you to apply on nested model, use key property instead
items = ndb.KeyProperty(kind=Mjlistitem, repeated=True)
class MjlistitemJob(Mjlistitem):
pass
class MjlistitemJobGeneric(MjlistitemJob):
jobtype = ndb.IntegerProperty()
class MjlistitemJobApp(MjlistitemJob):
appleid = ndb.StringProperty()
The Usage
def test():
rootfolder = MjlistitemFolder(title="root")
subfolder = MjlistitemFolder(title="Cool things")
subfolder.items.append(MjlistitemJobApp(appleid="281796108").put()) # evernote
subfolder.items.append(MjlistitemJobGeneric(jobtype=3).put()) # phone number
subfolder.items.append(MjlistitemJobApp(appleid="327630330").put()) # dropbox
rootfolder.items.append(MjlistitemJobGeneric(jobtype=15).put()) # passbook
rootfolder.items.append(subfolder.put())
rootfolder.items.append(MjlistitemJobGeneric(jobtype=17).put()) # appstore
rootfolder.put()
another thing should watch out.
the repeated property won't perform well if there are too many items in one folder, so it would be better if
class Mjlistitem(polymodel.PolyModel):
parent = ndb.KeyProperty(kind="Mjlistitem")
def set_parent(self, parent):
assert isinstance(parent, MjlistitemFolder)
self.parent = parent.key
class MjlistitemFolder(Mjlistitem):
title = ndb.StringProperty()
class MjlistitemJob(Mjlistitem):
pass
class MjlistitemJobGeneric(MjlistitemJob):
jobtype = ndb.IntegerProperty()
class MjlistitemJobApp(MjlistitemJob):
appleid = ndb.StringProperty()
The Usage
def test():
rootfolder = MjlistitemFolder(title="root")
rootfolder.put()
subfolder = MjlistitemFolder(title="Cool things")
subfolder.set_parent(rootfolder)
subfolder.put()
a = MjlistitemJobApp(appleid="281796108")
a.set_parent(subfolder)
a.put()
b = MjlistitemJobGeneric(jobtype=3)
b.set_parent(subfolder)
b.put()
c = MjlistitemJobApp(appleid="327630330")
c.set_parent(subfolder)
c.put()

Google app engine python problem

I'm having a problem with the datastore trying to replicate a left join to find items from model a that don't have a matching relation in model b:
class Page(db.Model):
url = db.StringProperty(required=True)
class Item(db.Model):
page = db.ReferenceProperty(Page, required=True)
name = db.StringProperty(required=True)
I want to find any pages that don't have any associated items.
You cannot query for items using a "property is null" filter. However, you can add a boolean property to Page that signals if it has items or not:
class Page(db.Model):
url = db.StringProperty(required=True)
has_items = db.BooleanProperty(default=False)
Then override the "put" method of Item to flip the flag. But you might want to encapsulate this logic in the Page model (maybe Page.add_item(self, *args, **kwargs)):
class Item(db.Model):
page = db.ReferenceProperty(Page, required=True)
name = db.StringProperty(required=True)
def put(self):
if not self.page.has_items:
self.page.has_items = True
self.page.put()
return db.put(self)
Hence, the query for pages with no items would be:
pages_with_no_items = Page.all().filter("has_items =", False)
The datastore doesn't support joins, so you can't do this with a single query. You need to do a query for items in A, then for each, do another query to determine if it has any matching items in B.
Did you try it like :
Page.all().filter("item_set = ", None)
Should work.

How to change the default property on a set in python and Google AppEngine

In the following code:
class ClassA(db.Model):
name = db.StringProperty()
class ClassB(db.Model):
name = db.StringProperty()
deleted_flag = db.BooleanProperty()
classA = db.ReferenceProperty(ClassA)
ClassA will have a property called classb_set. When I call classb_set within code, I do not want it to return items that have the deleted_flag = true.
How can I change the default filter on the classb_set query? My first instinct is to create another method in ClassA that does this filter, but is there a way that keeps the classb_set a property?
You can always use a Python property to accomplish your goal:
class ClassA(db.Model):
name = db.StringProperty()
def __get_classBdeleted(self):
return self.classB_set.filter('deleted_flag =', 'True')
classBdeleted = property(__get_classBdeleted)
class ClassB(db.Model):
name = db.StringProperty()
deleted_flag = db.BooleanProperty()
classA = db.ReferenceProperty(ClassA)

Categories

Resources