Say you have a concept of "user" records that you'd like to store in the data store.
class User (db.Model):
first_name = db.StringProperty()
last_name = db.StringProperty()
created = db.DateTimeProperty(auto_now_add=True)
twitter_oauth_token = db.StringProperty()
twitter_oauth_secret = db.StringProperty()
There are some fields you'd like to use almost ever time you use a user object, like first_name and last_name.
However, there are some fields you only have one use case for, for example, twitter_oauth_token and twitter_oauth_secret, and it's somewhat inefficient to bother serializing and deserializing these when they're not needed 95% of the time.
So if you split your model up:
class User (db.Model):
first_name = db.StringProperty()
last_name = db.StringProperty()
created = db.DateTimeProperty(auto_now_add=True)
class UserTwitterOauth(db.Model):
oauth_token = db.StringProperty(required=True)
oauth_secret = db.StringProperty(required=True)
created = db.DateTimeProperty(auto_now_add=True)
You can put a ReferenceProperty to the User in the UserTwitterOauth, but this would actually be one-to-many as there's nothing stopping there to being multiple UserTwitterOauth objects per User. You want there to be at most one UserTwitterOauth related to any User. How can you relate these models on a one-to-one basis?
In this specific case, your best option is probably to make the UserTwitterOauth entity a child of the User entity with a well-known key name, like so:
my_user = User(first_name="John", last_name="Smith")
my_user.put()
extra_info = UserTwitterOauth(parent=my_user, key_name="UserTwitterOauth")
extra_info.put()
You can add a straightforward method or property to the User class to make it easy to retrieve the additional information, and a class method to UserTwitterOauth to serve as a factory method, preserving the convention.
Incidentally, note that User is a dangerous name for an entity - the Users API has a class called User too, and unless you're very careful with your imports, you may end up referring to one when you intend to refer to the other.
A reference property to the user from the twitter access token is by far the easiest to maintain, in my view. It is true that the user could be referenced by many access tokens.
You will however find yourself doing things by convention a lot of times when working GAE.
EDIT preventing several access tokens referencing same user:
You can access referencing access tokens as a query via the User.usertwitteroauth_set property. If you want a more descriptive name, specify the parameter collection_namewhen setting up the ReferenceProperty. Say for example you want to remove any referencing access tokens before you add a new one, you could gather that logic as such:
class User(db.Model):
def set_access_token(self, access_token):
db.delete(self.twitter_access_tokens) # Think this should work, otherwise iterate over the query.
new_access_token.user = self
new_access_token.put()
class UserTwitterOauth(db.Model):
user = db.ReferenceProperty(User, collection_name = 'twitter_access_tokens')
Related
I have built a project with a React frontend and a Django Rest Framework (DRF) API for my backend. Things are going well but I am quite curious about best practices for data saved against your models.
As an example, I have a user model with first_name and last_name fields. There is a one-to-one relationship between my user and the two user domains (let's say Buyer and Seller as an example). In addition, we have a foreign key relationship for Seller on something like a Product.
On my UI, I have tables that display the data and in general, the majority of tables display the user's full name (first_name + last_name).
Generally, all table fields I will want to filter by and order by via API (eg. {base_url}/product/?ordering=full_name).
I decided that I wanted the data returned from the REST API to represent the table data so I am now returning full_name by augmenting the serializer of Buyer and Seller to have full name using a SerializerMethodField like so:
full_name = serializers.SerializerMethodField()
...
def get_full_name(self, obj) -> str:
return obj.user.get_full_name()
However, I will also need to do this in all places where I want to show the Buyer/Seller where they are referenced by a Foreign Key.
So let's say we need it in the Product, I would have something like:
seller_first_name = serializers.CharField(source='seller.user.first_name')
seller_last_name = serializers.CharField(source='seller.user.last_name')
seller_full_name = serializers.SerializerMethodField()
...
def get_seller_full_name(self, obj) -> str:
return obj.seller.user.get_full_name()
I guess I'm curious if it would be best to store all of these fields directly on the user object (first_name, last_name, and full_name). There is clearly some duplication here, so I assume Django would have a way for me to auto-write full_name based on first_name and last_name without requiring it to be passed in the API.
I'm sure it can be done either way but this is my first project using DRF as a backend and I would rather hear some other's experience now rather than learn myself in a year and have to do a large refactoring later on.
Thanks for any advice in advance.
All the best,
Brandon
You can define a property in your model:
class YourModel(models.Model):
...
#property
def full_name(self):
return f'{self.first_name} {self.last_name}'
Then you can access this in any serializer using source keyword argument:
full_name = serializers.CharField(source='YourModel.full_name')
Here's the doc
I'm trying to learn Google App Engine's NDB and I'm confused about the structure of models.
My situation is similar to a CMS platform with Post Types (like in WordPress), so I have "Blogs" and "Pages". All of these Post Types require the same set of attributes: Parent, Name, Slug, Template, Content, Status, and Date.
So far, I gather that I need to create a Model for these like this:
class Post(ndb.Expando):
parent = ndb.StringProperty()
name = ndb.StringProperty()
slug = ndb.StringProperty()
template = ndb.StringProperty()
content = ndb.StringProperty(indexed=False)
status = ndb.StringProperty()
date = ndb.DateTimeProperty(auto_now_add=True)
(I'm using Expando because I will be adding "unknown" attributes in my application)
But with this structure, all of my posts (in every Post Type) will be within the same "kind", so queries will take longer (if I'm not mistaken).
How can I create many Models (kinds) with the same attributes?
Do I copy & paste the above Model under different class names?
Is it possible to create new Models dynamically (similar to "Custom Post Types" in WordPress)? Does it work if I use ndb.Key('Blog', blogid) instead of declaring a Model?
Do I create a Model called class PostType(ndb.Model) that stores the "Post Types" and give them ancestors of Posts? (If I'm not mistaken, this would cause problems because updating a Post would "lock" the entire ancestor tree for a second or so)
My primary goal is efficiency. Thanks!
Updates:
As written by Dan and mgilson, adding sub-classes of the main Post class Model is a good way to solve this:
class Post(ndb.Expando):
parent = ndb.StringProperty()
name = ndb.StringProperty()
slug = ndb.StringProperty()
template = ndb.StringProperty()
content = ndb.StringProperty(indexed=False)
status = ndb.StringProperty()
date = ndb.DateTimeProperty(auto_now_add=True)
class Blog(Post):
pass
However, this requires writing the Models statically. Is there a way to accomplish this dynamically (without declaring them as Models beforehand)?
Update:
Following the advice given below, I decided to keep all of my entities under the same kind. I might decide later on to change this to subclasses (separate kinds for each "Post Type") if my queries get messy. Thank you all for your great advice!
How can I create many Models with the same attributes?
You can subclass:
class SpecialPost(Post):
"""Special post type that is a different kind than Post."""
Though it's often easy enough to use the same kind and just add an extra field that represents the kind of post which you can filter on in queries.
Is it possible to create new Models dynamically (similar to "Custom Post Types" in WordPress)? Does it work if I use ndb.Key('Blog', blogid) instead of declaring a Model?
I'm not 100% sure that I understand what you're asking here. You can dynamically create models the same way you can dynamically create classes in python (using type), but you probably don't want to be doing this. Getting those dynamically created models (and keeping track of their names) will probably end up giving you serious headaches.
Basically a simple example of subclassing, which #mgilson mentioned already.
class Post(ndb.Expando):
parent = ndb.StringProperty()
name = ndb.StringProperty()
slug = ndb.StringProperty()
template = ndb.StringProperty()
content = ndb.StringProperty(indexed=False)
status = ndb.StringProperty()
date = ndb.DateTimeProperty(auto_now_add=True)
class Blog(Post):
someint = ndb.IntegerProperty()
blog = Blog(status='new', someint=2)
key = blog.put()
print key.kind()
As for dynamically creating models, from the Model's Constructor doc:
An application won't normally call Model(), but is likely to call the
constructor of a class that inherits from Model. This creates a new
instance of this model, also known as an entity.
Even if possible (I didn't dig too deep inside ndb/models.py to say with certainty that it's not) it doesn't appear a clear thing. Personally I'd stay away from that and instead re-think the need for such dynamically created models.
Following this answer, I tried to split my SQL Story table into parent/children - with the children holding the specific user data, the parent more generic data. Now I've run into a problem that betrays my lack of experience in Django. My user page attempts to show a list of all the stories that a user has written. Before, when my user page was only pulling data from the story table, it worked fine. Now I need to pull data from two tables with linked info and I just can't work out how to do it.
Here's my user_page view before attempts to pull data from the parent story table too:
def user_page(request, username):
user = get_object_or_404(User, username=username)
userstories = user.userstory_set.order_by('-id')
variables = RequestContext(request, {
'username': username,
'userstories': userstories,
'show_tags': True
})
return render_to_response('user_page.html', variables)
Here is my models.py:
class story(models.Model):
title = models.CharField(max_length=400)
thetext = models.TextField()
class userstory(models.Model):
main = models.ForeignKey(story)
date = models.DateTimeField()
user = models.ForeignKey(User)
I don't really know where to start in terms of looking up the appropriate information in the parent table too and assinging it to a variable. What I need to do is follow the 'main' Key of the userstory table into the story table and assign the story table as a variable. But I just can't see how to implement that in the definition.
EDIT: I've tried story = userstory.objects.get(user=user) but I get 'userstory matching query does not exist.'
Reading through your previous question that you linked to, I've discovered where the confusion lies. I was under the impression that a Story may have many UserStorys associated with it. Note that I'm using Capital for the class name, which is common Python practise. I've made this assumption because your model structure is allowing this to happen with the use of a Foreign Key in your UserStory model. Your model structure should look like this instead:
class Story(models.Model):
title = models.CharField(max_length=400)
thetext = models.TextField()
class UserStory(models.Model):
story = models.OneToOneField(Story) # renamed field to story as convention suggests
date = models.DateTimeField()
user = models.ForeignKey(User)
class ClassicStory(models.Model)
story = models.OneToOneField(Story)
date = models.DateTimeField()
author = models.CharField(max_length=200)
See the use of OneToOne relationships here. A OneToOne field denotes a 1-to-1 relationship, meaning that a Story has one, and only one, UserStory. This also means that a UserStory is related to exactly one Story. This is the "parent-child" relationship, with the extra constraint that a parent has only a single child. Your use of a ForeignKey before means that a Story has multiple UserStories associated with it, which is wrong for your use case.
Now your queries (and attribute accessors) will behave like you expected.
# get all of the users UserStories:
user = request.user
stories = UserStory.objects.filter(user=user).select_related('story')
# print all of the stories:
for s in stories:
print s.story.title
print s.story.thetext
Note that select_related will create a SQL join, so you're not executing another query each time you print out the story text. Read up on this, it is very very very important!
Your previous question mentions that you have another table, ClassicStories. It should also have a OneToOneField, just like the UserStories. Using OneToOne fields in this way makes it very difficult to iterate over the Story model, as it may be a "ClassicStory" but it might be a "UserStory" instead:
# iterate over ALL stories
allstories = Story.objects.all()
for s in allstories:
print s.title
print s.thetext
print s.userstory # this might error!
print s.classicstory # this might error!
See the issue? You don't know what kind of story it is. You need to check the type of story it is before accessing the fields in the sub-table. There are projects that help manage this kind of inheritance around, an example is django-model-utils InheritanceManager, but that's a little advanved. If you never need to iterate over the Story model and access it's sub tables, you don't need to worry though. As long as you only access Story from ClassicStories or UserStories, you will be fine.
I've made two django models: Person and Items (here is part of my code):
class Person(models.Model):
""" Represent a person who has credentials. The person may have
devices and/or accessories. """
#basic information
name = models.CharField(max_length=50, unique=True)
class Item(models.Model):
""" Represents a device or accessory. """
owner = models.ForeignKey(Person)
Basically, I need to have one person have the potential to own multiple items. I need to be able to check whether or not an owner has an item and then I also need to be able to check if an item currently has an owner. I can't work with the variable owner because it's a foreign key. Or maybe I'm solving this problem incorrectly?
If I'm approaching this wrong, I need to be able to store a database of items and owners, with owners having multiple items, and I should be able to know who has what items.
Please help!
I think you need to declare Person before Item
Also you might want to use a ManyToMany relationship instead of a ForeignKey
class Item(models.Model):
""" Represents a device or accessory. """
name = models.models.CharField(max_length=50)
class Person(models.Model):
""" Represent a person who has credentials. The person may have
devices and/or accessories. """
#basic information
name = models.models.CharField(max_length=50)
item = models.ManyToManyField(Item)
So basically you were doing it backwards.
So you create an object Item:
item = Item.objects.create(name='itemname')
And you add it to a person object:
person = Person.objects.get(name="the person's name")
person.add(item)
person.save
This should work
I'm building a personal project with Django, to train myself (because I love Django, but I miss skills). I have the basic requirements, I know Python, I carefully read the Django book twice if not thrice.
My goal is to create a simple monitoring service, with a Django-based web interface allowing me to check status of my "nodes" (servers). Each node has multiple "services". The application checks the availability of each service for each node.
My problem is that I have no idea how to represent different types of services in my database. I thought of two "solutions" :
single service model, with a "serviceType" field, and a big mess with the fields. (I have no great experience in database modeling, but this looks... "bad" to me)
multiple service models. i like this solution, but then I have no idea how I can reference these DIFFERENT services in the same field.
This is a short excerpt from my models.py file : (I removed everything that is not related to this problem)
from django.db import models
# Create your models here.
class service(models.Model):
port = models.PositiveIntegerField()
class Meta:
abstract = True
class sshService(service):
username = models.CharField(max_length=64)
pkey = models.TextField()
class telnetService(service):
username = models.CharField(max_length=64)
password = models.CharField(max_length=64)
class genericTcpService(service):
pass
class genericUdpService(service):
pass
class node(models.Model):
name = models.CharField(max_length=64)
# various fields
services = models.ManyToManyField(service)
Of course, the line with the ManyToManyField is bogus. I have no idea what to put in place of "*Service". I honestly searched for solutions about this, I heard of "generic relations", triple-join tables, but I did'nt really understand these things.
Moreover, English is not my native language, so coming to database structure and semantics, my knowledge and understanding of what I read is limited (but that's my problem)
For a start, use Django's multi-table inheritance, rather than the abstract model you have currently.
Your code would then become:
from django.db import models
class Service(models.Model):
port = models.PositiveIntegerField()
class SSHService(Service):
username = models.CharField(max_length=64)
pkey = models.TextField()
class TelnetService(Service):
username = models.CharField(max_length=64)
password = models.CharField(max_length=64)
class GenericTcpService(Service):
pass
class GenericUDPService(Service):
pass
class Node(models.Model):
name = models.CharField(max_length=64)
# various fields
services = models.ManyToManyField(Service)
On the database level, this will create a 'service' table, the rows of which will be linked via one to one relationships with separate tables for each child service.
The only difficulty with this approach is that when you do something like the following:
node = Node.objects.get(pk=node_id)
for service in node.services.all():
# Do something with the service
The 'service' objects you access in the loop will be of the parent type.
If you know what child type these will have beforehand, you can just access the child class in the following way:
from django.core.exceptions import ObjectDoesNotExist
try:
telnet_service = service.telnetservice
except (AttributeError, ObjectDoesNotExist):
# You chose the wrong child type!
telnet_service = None
If you don't know the child type beforehand, it gets a bit trickier. There are a few hacky/messy solutions, including a 'serviceType' field on the parent model, but a better way, as Joe J mentioned, is to use a 'subclassing queryset'. The InheritanceManager class from django-model-utils is probably the easiest to use. Read the documentation for it here, it's a really nice little bit of code.
I think one approach that you might consider is a "subclassing queryset". Basically, it allows you to query the parent model and it will return instances of the child models in the result queryset. It would let you do queries like:
models.service.objects.all()
and have it return to you results like the following:
[ <sshServiceInstance>, <telnetServiceInstance>, <telnetServiceInstance>, ...]
For some examples on how to do this, check out the links on the blog post linked below.
http://jazstudios.blogspot.com/2009/10/django-model-inheritance-with.html
However, if you use this approach, you shouldn't declare your service model as abstract as you do in the example. Granted, you will be introducing an extra join, but overall I've found the subclassing queryset to work pretty well for returning a mixed set of objects in a queryset.
Anyway, hope this helps,
Joe
If you are looking for generic foreign key relations you should check the Django contenttypes framework (built into Django). The docs pretty much explain how to use it and how to work with generic relations.
An actual service can only be on one node, right? In that case when not have a field
node = models.ForeignKey('node', related_name='services')
in the service class?