I defined an expense model for an expense application,
class Expense(models.Model):
pub_date = models.DateTimeField()
amount = models.IntegerField()
memo = models.TextField()
and I would like to create lost of separate tables to maintain data for different users, such as
james_expense_table for james
william_expense_table for william
....
balabala_expense_table for balabala
They are exactly same behavior, like parallel objects. The only difference is the prefix. So that, i can easily extend the application with more features.
So how can I achieve this?
I've read some abstract model stuff from django web. But in fact, they are static, hard coded in the *.py files, not what i want.
And one more question, for a static model (hard code in *.py file), it can use "manage.py syncdb" command to sync the module fields to the table fields, so how can do this for the dynamic case?
What you want is probably to use a ForeignKey so that you can link your table to different users:
class Expense(models.Model):
pub_date = models.DateTimeField()
amount = models.IntegerField()
memo = models.TextField()
user = models.ForeignKey(MyUserField)
Obviously you need to have a MyUserField implemented and imported.
You can then access the user from the Expense table
my_expense.user
Or the Expense table from the user using:
my_user.expense_set.all()
You then don't require to run syncdb for every new user, and it's not statically hard-coded in the file.
Related
I am writing an app for GAE in Python which stores recipes for different users. I have an entity called User in the datastore and an entity called Recipe. I want to be able to set the owner of each Recipe to the User who created it. Also, I want each User entity to contain a list of all Recipes belonging to that User as well as being able to query the Recipe database to find all Recipes owned by a particular User.
What is the best way to go about creating this parent/child type relationship?
Thanks
There are two main ways. (I am going to assume your using python which defines examples)
Option 1. Make the User the ancestor of all of their recipes
recipe = Recipe(parent=user.key)
Option 2. Use key property
class Recipe(ndb.Model):
owner = ndb.KeyProperty()
recipe = Recipe(owner=user.key)
all recipes for user with option 1
recipes = Recipe.query(ancestor=user.key)
all recupes for user with option 2
recipes = Recipe.query().filter(Recipe.owner == user.key)
Which one you use really depends a lot on what you plan to do with the data after creation, transaction patterns etc.... You should elaborate on your use cases. Both will work.
Also you should read up on transactions entity groups and understand them to really determine if you want to use ancestors https://developers.google.com/appengine/docs/java/datastore/transactions?hl=en .
If you use db.Model, to model one-to-many relationship, you can use the RefernenceProperty constructor and specify a collection_name. For example, one book may have many reviews.
class Book(db.Model):
title = db.StringProperty()
author = db.StringProperty()
class BookReview(db.Model):
book = db.ReferenceProperty(Book, collection_name='reviews')
b = Book()
b.put()
br = BookReview()
br.book = b # sets br's 'book' property to b's key
for review in b.reviews:# use collection_name to retrieve all reviews for a book
....
see https://developers.google.com/appengine/docs/python/datastore/datamodeling#references
Alternatively, you can use ndb's KeyProperty as in Tim's answer.
Also see
db.ReferenceProperty() vs ndb.KeyProperty in App Engine
I am reading up on Google app engine and preparing a sample to understand it better.
In a nutshell the user can record an entry for every day in the month, like a calendar.
And the user can view the entries on monthly basis. So no more than 30 ish at a time.
Initially I had used db and the one-to-many relationship was straight forward.
But once I came across the ndb, I realized there are two ways of modelling a one-to-many relationship.
1) The structured property seems to act like a repeated property on the User model. Does it mean if I retrieve one user, I would automatically retrieve all the records she has entered? (e.g. the entire year) This isn't very efficient though, is it? I guess the the advantage is that you get all related data in one read operation.
from google.appengine.ext import ndb
class User(UserMixin, ndb.Model):
email = ndb.StringProperty(required = True)
password_hash = ndb.TextProperty(required = True)
record = ndb.StructuredProperty(Record, repeated=True)
class Record(ndb.Model):
notes = ndb.TextProperty()
2) Alternatively I could use perhaps the more classic way:
class User(UserMixin, ndb.Model):
email = ndb.StringProperty(required = True)
password_hash = ndb.TextProperty(required = True)
class Record(ndb.Model):
user = ndb.KeyProperty(kind=User)
notes = ndb.TextProperty()
Which way is the better way in my use case?
The downside of using StructuredProperty instead of KeyProperty is that with StructuredProperty the limit on total entity size (1MB) applies to the sum of the User and all Records it contains (because the structured properties are serialized as part of the User entity). With KeyProperty, each Record has a 1MB limit by itself.
I'm just starting to get my head around non-relational databases, so I'd like to ask some help with converting these traditional SQL/django models into Google App Engine model(s).
The example is for event listings, where each event has a category, belongs to a venue, and a venue has a number of photos attached to it.
In django, I would model the data like this:
class Event(models.Model)
title = models.CharField()
start = models.DatetimeField()
category = models.ForeignKey(Category)
venue = models.ForeignKey(Venue)
class Category(models.Model):
name= models.CharField()
class Venue (models.Model):
name = models.CharField()
address = models.CharField()
class Photo(models.Model):
venue = models.ForeignKey(Venue)
source = models.CharField()
How would I accomplish the equivalent with App Engine models?
There's nothing here that must be de-normalized to work with App Engine. You can change ForeignKey to ReferenceProperty, CharField to StringProperty and DatetimeField to DateTimeProperty and be done. It might be more efficient to store category as a string rather than a reference, but this depends on usage context.
Denormalization becomes important when you start designing queries. Unlike traditional SQL, you can't write ad-hoc queries that have access to every row of every table. Anything you want to query for must be satisfied by an index. If you're running queries today that depend on table scans and complex joins, you'll have to make sure that the query parameters are indexed at write-time instead of calculating them on the fly.
As an example, if you wanted to do a case-insensitive search by event title, you'd have to store a lower-case copy of the title on every entity at write time. Without guessing your query requirements, I can't really offer more specific advice.
It's possible to run Django on App Engine
You need a trio of apps from here:
http://www.allbuttonspressed.com/projects
Django-nonrel
djangoappengine
djangotoolbox
Additionally, this module makes it possible to do the joins across Foreign Key relationships which are not directly supported by datastore methods:
django-dbindexer
...it denormalises the fields you want to join against, but has some limitations - doesn't update the denormalised values automatically so is only really suitable for static values
Django signals provide a useful starting point for automatic denormalisation.
I think my understanding of Django's FK and admin is a bit faulty, so I'd value any input on how to model the below case.
Firstly, we have generic Address objects. Then, we have User's, who each have a UserProfile. Through this, Users belong to departments, as well as having addresses.
Departments themselves can also have multiple addresses, as well as a head of department. So it might be something like (this is something I'm just hacking up now):
class Address(models.Model):
street_address = models.CharField(max_length=20)
etc...
class Department(models.Model):
name = models.CharField(max_lenght=20)
head_of_department = models.OneToOneField(User)
address = models.ForeignKey(Address)
class UserProfile(models.Model):
user = models.ForeignKey(User, unique=True)
address = models.ForeignKey(Address)
department = models.OneToOneField(Department)
Anyhow, firstly, is that the right way of setting up the relationships?
And secondly, I'd like it to appear in the admin that you can edit a department, and on that page, it'd have an inline list of all the addresses to also edit. I've tried setting up an AddressInline class, and attaching it as an inline to Department.
class AddressInline(admin.TabularInline):
model = Address
class DepartmentAdmin(admin.ModelAdmin):
inlines = [AddressInline]
However, when I try to display that, I get:
Exception at /admin/people/department/1/
<class 'people.models.Address'> has no ForeignKey to <class 'people.models.Department'>
Cheers,
Victor
Since it seems you want a UserProfile or Department to have potentially many addresses, your ForeignKeys are backward. A single ForeignKey can only point to one model instance, whereas there is no limit on the number of ForeignKeys that can point to a single model instance. So your ForeignKey should be on Address (in which case your inline would work as-is).
The complicating factor is that you have a single Address model and you want to relate it to two other models; a single ForeignKey on Address can't point to both UserProfile and Department. One solution is to have two address models (DepartmentAddress, with a ForeignKey to Department, and UserAddress, with a ForeignKey to UserProfile). You could reduce duplication in your code by having these both inherit from an abstract base class containing all the data fields, but you still end up with two mostly-identical tables in your database.
The other option is to have a GenericForeignKey on Address, which can point to an instance of any model. Your inline would then need to become a GenericInlineModelAdmin. This violates pure database normalization and doesn't allow your database to do proper integrity checking. If you had potentially more models in the future that would also have addresses, I'd consider this; if it's likely to be limited to only the current two, I might go with the above option instead.
I read your models what you want from your models as:
A department has one to many addresses
A department has one and only one user (as head of department)
A user (through his profile) belongs to one to many departments
A user (through his profile) has one to many addresses
If that was your intent, meaning that there is no case where a user will NOT have an address or a department, and no case where a department will not have an address or a head of department; then I would say your models are OK should read:
class Department(models.Model):
name = models.CharField(max_lenght=20)
head_of_department = models.OneToOneField(User)
address = models.ForeignKey(Address)
class UserProfile(models.Model):
user = models.ForeignKey(User, unique=True)
address = models.ForeignKey(Address)
department = models.OneToOneField(Department)
class Address(models.Model):
street_address = models.CharField(max_length=20)
...
class Meta:
abstract = True
class UserAddress(Address):
user_profile = models.ForeignKey(UserProfile)
class DepartmentAddress(Address):
department = models.ForeignKey(Department)
Read more about abstract classes.
What your models are not contemplating are the possibilities that two Users will have the same address, and/or that two departments will have the same address. Since you are not specifying a unique constraint on address (that I can see), I assume that you are OK with a real-world address showing up more than once in your Address table.
If that is OK with you; fine.
The error message you are getting is stating a fact: there is no foreign key in Address to Department. You will have to revert that relationship for the inline to work. Meaning, when editing an address you can edit any departments associated with it; but not the reverse. With the models I suggest above you should not see this error.
See the example from the docs. Notice how an Author has many Books and how the many side of the relationship is the one that can be inline.
I saw the question can i use a database view as a model in django and tried it in my app, but that didn't work.
I created a view named "vi\_topics" manually and it had "id" column but I kept getting an error, even if I added "id" field explicitly, saying
"no such column: vi_topics.id"
Here is the definition of my model named Vitopic:
from django.db import models
class Vitopic(models.Model):
title = models.CharField(max_length=200)
content = models.TextField()
author_name = models.CharField(max_length=200)
author_email = models.CharField(max_length=200)
view_count = models.IntegerField(default=0)
replay_count = models.IntegerField(default=0)
tags = models.CharField(max_length=255)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class Meta:
db_table = 'vi_topics'
Note: I use sqlite3.
Try this:
http://docs.djangoproject.com/en/dev/ref/models/options/#managed
managed
Options.managed
New in Django 1.1: Please, see the release notes
Defaults to True, meaning Django will create the appropriate database tables in syncdb and remove them as part of a reset management command. That is, Django manages the database tables' lifecycles.
If False, no database table creation or deletion operations will be performed for this model. This is useful if the model represents an existing table or a database view that has been created by some other means. This is the only difference when managed is False. All other aspects of model handling are exactly the same as normal. This includes
Adding an automatic primary key field to the model if you don't declare it. To avoid confusion for later code readers, it's recommended to specify all the columns from the database table you are modeling when using unmanaged models.
If a model with managed=False contains a ManyToManyField that points to another unmanaged model, then the intermediate table for the many-to-many join will also not be created. However, a the intermediary table between one managed and one unmanaged model will be created.
If you need to change this default behavior, create the intermediary table as an explicit model (with managed set as needed) and use the ManyToManyField.through attribute to make the relation use your custom model.
For tests involving models with managed=False, it's up to you to ensure the correct tables are created as part of the test setup.
id = models.IntegerField(primary_key=True)