Google App Engine: Model integrity constraints? - database

I have a datastore model representing items in an ecommerce site:
class Item(db.Model):
CSIN = db.IntegerProperty()
name = db.StringProperty()
price = db.IntegerProperty()
quantity = db.IntegerProperty()
Is there some way to enforce integrity constraints? For instance, I would like to make sure that quantity is never set to be less than 0.

The Property constructor lets you specify a function with the 'validator' named argument. This function should take one argument, the value, and raise an exception if the valid is invalid. For example:
def range_validator(minval, maxval):
def validator(v):
if (minval is not None and v < minval) or (maxval is not None and v > maxval):
raise ValueError("Value %s outside range (%s, %s)" % (v, minval, maxval))
return validator
class Item(db.Model):
CSIN = db.IntegerProperty()
name = db.StringProperty()
price = db.IntegerProperty()
quantity = db.IntegerProperty(validator=range_validator(0, None))
Note that the example uses a nested function to define general-purpose validators - you can, of course, use simple functions if you want to write a more special purpose validator.

Related

Django Annotate and Aggregate

I'd like to Sum the post_value of all of the Posts for each post_user to eventually use in a chart. I'm struggling with how to formulate the query?
So far, I've got to:
user_totals = User.objects.annotate(post_value_total=Sum('post'))
models.py
class User(AbstractUser):
pass
class Post(models.Model):
post_user = models.ForeignKey(User, on_delete=models.CASCADE)
post_cat = models.ForeignKey(Category, on_delete=models.CASCADE)
post_action = models.ForeignKey(Action, on_delete=models.CASCADE)
post_quantity = models.PositiveIntegerField(blank=True, null=True)
post_value = models.PositiveIntegerField(default='0')
post_timestamp = models.DateTimeField(auto_now_add=True)
def __str__(self):
return f"{self.post_user}'s post at {self.post_timestamp}"
Thanks.
I'd like to Sum the post_value of all of the Posts for each post_user to eventually use in a chart.
Since each Post has a non-nullable post_user ForeignKey, it means that each Post belongs to exactly one user.
We thus can sum up the number of post_values of all Users with:
Post.objects.all().count()
If you only want to sum these up for a subset of the users, you can work with:
Post.objects.filter(
post_user__in=[user1, user2, user3]
).count()
or if you have ids:
Post.objects.filter(
post_user_id__in=[user_id1, user_id2, user_id3]
).count()
Or if you want to sum up the post_values, you can work with:
from django.db.models import Sum
total_post_value = Post.objects.aggregate(
total=Sum('post_value')
) or 0
The or 0 is necessary if the collection can be empty, since the sum of no records is NULL/None, not 0.
Or if you want to do this per User, we can work with:
user_totals = User.objects.annotate(
post_value_total=Sum('post__post_value')
)
The User objects that arise from this will have an extra attribute post_value_total that sums up the values of the related Posts. These can be None if a user has no related Posts. In that case we can work Coalesce [Django-doc]:
from django.db.models import Sum, Value
from django.db.models.functions import Coalesce
user_totals = User.objects.annotate(
post_value_total=Coalesce(Sum('post__post_value'), Value(0))
)

How to create an object attribute without writing to database -- peewee -- python

Maybe i have an understanding problem. I try to make 2 tabeles in one database. But additionaly i need to have some temporary values in one class that i doen´t want to write to the database.
I try to switch to peewee and read the dokumentation but i find no solution at my own.
without peewee i would make an init method where i write my attributes. But where did i have to write them now?
from peewee import *
import datetime
db = SqliteDatabase('test.db', pragmas={'foreign_keys': 1})
class BaseModel(Model):
class Meta:
database = db
class Sensor(BaseModel):
id = IntegerField(primary_key=True)
sort = IntegerField()
name = TextField()
#def __init__(self):
#self.sometemporaryvariable = "blabla"
def meineparameter(self, hui):
self.hui = hui
print(self.hui)
class Sensor_measure(BaseModel):
id = ForeignKeyField(Sensor, backref="sensorvalues")
timestamp = DateTimeField(default=datetime.datetime.now)
value = FloatField()
class Meta:
primary_key = CompositeKey("id", "timestamp")
db.connect()
db.create_tables([Sensor_measure, Sensor])
sensor1 = Sensor.create(id=2, sort=20, name="Sensor2")
#sensor1.sometemporaryvariable = "not so important to write to the database"
sensor1.save()
Remember to call super() whenever overriding a method in a subclass:
class Sensor(BaseModel):
id = IntegerField(primary_key=True)
sort = IntegerField()
name = TextField()
def __init__(self, **kwargs):
self.sometemporaryvariable = "blabla"
super().__init__(**kwargs)

django how to save a big model with for loop in views.py?

I have a very big model in models.py:
simplified version is:
class MyModel(models.Model):
item_1 = models.FloatField(null=True, blank=True)
...
item_20 = models.FloatField(null=True, blank=True)
in views.py:
def form_valid(self, form_class):
instance = form_class.save(commit=False)
for i in range(1, 20):
name = 'item_' + str(i)
instance.name = i
With this the field name 'item_1' ... to 'item_20' in instance is not recogniced. Instead 'name' is added to instance like other new field...
How can I iterate and save my model?
Any suggestion?
Thanks!!!
You should probably use setattr in order to loop through the fields and set the values in them. Try this:
def form_valid(self, form_class):
instance = form_class.save(commit=False)
for i in range(1, 20):
name = 'item_' + str(i)
setattr(instance, name, value) # Where value is the data you wanted to save in the field `name`
Similary user getattr() to get the data by looping through the class instance.

value from SELECT command is object

I am doing a project in google app engine. Python is used in the back end. I have a datastore table "Data" with following attributes,
class Data(db.Model):
url = db.StringProperty
code = db.StringProperty
turl = db.StringProperty
I used following lines of code to get all values from the table,
x = Data.all()
x = db.GqlQuery("SELECT * FROM Data")
ourl = x.fetch(10)
When i print it using the following code,
for p in ourl:
print "%s %s, %s " % (p.url, p.code, p.turl)
i got 10 times the following message,
<class 'google.appengine.ext.db.StringProperty'> <class 'google.appengine.ext.db.StringProperty'>, <class 'google.appengine.ext.db.StringProperty'>
I cannot get the real values of url,code and turl. What to do with this code??
In your class Data you forgot parenthesis to create instances of property.
class Data(db.Model):
url = db.StringProperty()
code = db.StringProperty()
turl = db.StringProperty()
Currently your simply copy the class db.StringProperty in your attributes defined in your class Data

effective counting of objects

I have 2 models:
Category(models.Model):
name = models.CharField(max_length=30)
no_of_posts = models.IntegerField(default=0) # a denormalised field to store post count
Post(models.Model):
category = models.ForeignKey(Category)
title = models.CharField(max_length=100)
desc = models.TextField()
user = models.ForeignKey(User)
pub_date = models.DateTimeField(null=True, blank=True)
first_save = models.BooleanField()
Since I always want to show the no. of posts alongwith each category, I always count & store them every time a user creates or deletes a post this way:
## inside Post model ##
def save(self):
if not pub_date and first_save:
pub_date = datetime.datetime.now()
# counting & saving category posts when a post is 1st published
category = self.category
super(Post, self).save()
category.no_of_posts = Post.objects.filter(category=category).count()
category.save()
def delete(self):
category = self.category
super(Post, self).delete()
category.no_of_posts = Post.objects.filter(category=category).count()
category.save()
........
My question is whether, instead of counting every object, can we not use something like:
category.no_of_posts += 1 // in save() # and
category.no_of_posts -= 1 // in delete()
Or is there a better solution!
Oh, I missed that! I updated the post model to include the relationship!
Yes, a much better solution:
from django.db.models import Count
class CategoryManager(models.Manager):
def get_query_set(self, *args, **kwargs):
qs = super(CategoryManager, self).get_query_set(*args, **kwargs)
return qs.annotate(no_of_posts=Count('post'))
class Category(models.Model):
...
objects = CategoryManager()
Since you didn't show the relationship between Post and Category, I guessed on the Count('posts') part. You might have to fiddle with that.
Oh, and you'll want to get rid of the no_of_posts field from the model. It's not necessary with this. Or, you can just change the name of the annotation.
You'll still be able to get the post count with category.no_of_posts but you're making the database do the legwork for you.

Categories

Resources