Django - Designing Model Relationships - Admin interface and Inline - database

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.

Related

Is a Value Object that is used many times an entity?

The question might not be very clear in the title, let me explain:
In my model, I have an Person, that has an Address. However, many Persons can share the same Address.
As I was defining my model, I assumed that Person is an Entity, but Address a Value-Object since if you change a single property of the Address, well it's not the same Address anymore.
Since multiple Persons can share an Address, if I jump right into the database implementation, and naively assume that person has some address_xxxx fields, wouldn't it generate too many duplicates in the database ? Isn't it better that person has an address_id field, related to an address table ? If so, then Address is an Entity right ?
Is a Value Object that is used many times an entity?
No, but it depends...
It is often the case that a value object is actually a proxy identifier for an entity, that you may not have explicitly realized in your model.
For example:
1600 Pennsylvania Ave NW
Washington, DC
20500
If you look at that carefully, you'll see embedded in it
The name of a street
The name of a city
If those are references to a street/city entities in your model, then "address" is the representation of the current state of some entity (ex: "The White House").
Complicating things further - you want suitable abstractions for your model.
Consider money:
{USD:100}
That's a value type, we can replace any USD:100 with a "different" USD:100
{USD:100, SerialNumber:KB46279860I}
That's still a value (it's state), but it the state of a specific bill that exists in circulation (somewhere). What we have here is an information resource that is describing an entity out in the real world, somewhere.
You also need to be careful about coincident properties. For example; the name of the street changes -- should the value of address change? If the model cares about the current identifier of a location, then perhaps it should. If the model is tracking what information you put on an envelope two months ago, then it certainly shouldn't. (In other words, when we changed the label for the street entity, the label already printed on the envelope entity didn't change).
It's an important question, but the answer changes depending on what you are modeling at the time.
In my model, I have an Person, that has an Address. However, many
Persons can share the same Address.
Isn't it better that person has an address_id field, related to an
address table ? If so, then Address is an Entity right?
You have to recognize that there are two distinct models, a domain model and a persistence model and both may not agree on whether a concept is an entity or a value.
The first thing you have to do is ask yourself what is an address from the domain perspective? Is your domain interested in the lifecycle of addresses or they are just immutable values? For instance, what happens if there is a typo in an address? Do you simply discard the incorrect one and replace it or would you rather modify the original address details to track it's continuity? These questions will help you to determine whether an address is an entity or a value from the domain perspective.
Now, a concept may be a value in the domain while being an entity in the persistence model. For instance, let's say that you aren't interested in the lifecycle of addresses in the domain, but you are very concerned about optimizing the storage space. In that case, you could give identifiers to unique addresses in the DB and use that for relationships rather than copying the same address details multiple times.
However, doing so would introduce additional tensions between your models, so you must be sure that there are real benefits to do so.

Complicated, costly many-to-many entity references

I have four main kinds: Account, Company, Service and Address. I would like Address entities to be shared between Company and Service entities.
Account: this is the user account (email, password)
Company: A business which provide Services (ancestor: Account)
Service: A service rendered by a Company (ancestor: Company)
Address: An address (group of fields: street, city, country) of a Company OR a Service (ancestor: Account)
The Challenge:
Company and Service entities may have different addresses; after all, a company's address is not necessarily where its services are acquired. Services may have many addresses, since a company may set up different franchises/outlets where its services may be acquired.
I would like to model data in such a way that Addresses can be referenced by either Company or Service entities, or both. I have tried these two approaches:
Let's assume this is the Address model:
class Address(ndb.Model):
street = ndb.StringProperty(required=True)
city = ndb.StringProperty(required=True)
country = ndb.StringProperty(required=True)
Approach 1: Store list of address keys inside Service or Company
class Service(ndb.Model):
title = ndb.StringProperty(required=True)
addresses = ndb.KeyProperty(repeated=True)
class Company(ndb.Model):
name = ndb.StringProperty(required=True)
addresses = ndb.KeyProperty(repeated=True)
Problem: For each page view of Service or Company, I would need to perform additional queries to fetch the their respective addresses. This blows up to be a big expensive problem as our entities grow in number.
Approach 2: Create an AddressMapping entity which forms a relationship between two entities:
class Service(ndb.Model):
title = ndb.StringProperty(required=True)
addresses = ndb.KeyProperty(repeated=True)
class AddressMapping(ndb.Model):
entity = ndb.StringProperty(required=True) # service or company
address = ndb.KeyProperty(repeated=True)
Problem: If a service is disabled/deleted/modified, we need to delete/modify all accompanying AddressMapping entities, or else they will be orphaned. Additional queries still required when viewing pages. This also seems expensive.
These are the two approaches I've come up with; they both seem bad. Any ideas on how I may improve this?
If you store keys of addresses in your Company and Service models, you do not need "additional queries to fetch them" - you can simply get all address entities that you need. This is fast and cheap.
This is a pretty standard problem with the datastore. The solution is denormalisation. By allowing duplicates you can break the problem down into a one-to-many relationship. So in your example: Allow Address duplicates and let each Address have a parent, either Company or Service. Or split your Address entity into two (ServiceAddress, CompanyAddress).
When you now modify a Service or Company, you can do a simple ancestor query for the addresses and you will only get the corresponding addresses.
This approach assumes that you will not update Address (or any other entity you have) more than once per second, since you will run into the 1 write per second and entity group otherwise.

Object Relation Class Diagram

I am designing a Object Relational Database and here is my Class Diagram:
http://canning.co.nz/AdvancedDatabase/Class_Diagram.png
My Computer Class has a 1-1 relationship with the CurrentUser Class. When creating the Object in code, the Computer Class has a CurrentUser Object as an attribute (CurrentUser_objtyp).
Here is the code:
create type Computer_objtyp as Object (
CompNo Number,
CompName Varchar2(20),
CompOS Varchar2(20),
CompProcessor Varchar2(20),
CompRAM Varchar2(20),
CurrentUser_obj CurrentUser_objtyp,
HardDriveList_var HardDriveList_Vartyp,
member function getCompName return varchar2)
/
My question is this:
As the Computer Class has this attribute, does the Computer Class need the attribute listed with the other attributes (CompNo, CompName, CompOS, CompProcessor, CompRAM), or is the relationship link to the CurrentUser Class sufficient?
It all depends on the exact definitions of computer and currentUser. A few examples:
If you perceive a computer as the machine, then does it have no current user, because it needs no user at all. Users belong to the OS.
If you see the current user as the person who has hired computer time on this computer, then is it important.
If you see the current user as the current owner of the computer, then might it be otherwise and could the computer be a feature belonging to the user.
If the current user is an employee of a company and leasing the computer for his work, then is it important to have a link from the computer to the combination company/user, hence could you have a reference to a contract.
You have used a compositional relationship to describe the relationship between the computer and the user. That implies that a computer has a currentuser and that the life span of the current user depends on the life span of the computer. Then should the current user directly or indirectly be part of the list of variables of the computer.
On the ohter hand have you also a compositional relationship between a peripherical device and the computer. That is hard to understand. An association is the most likely relationship between a peripherical device and a computer.

NDB Modeling One-to-one with KeyProperty

I'm quite new to ndb but I've already understood that I need to rewire a certain area in my brain to create models. I'm trying to create a simple model - just for the sake of understanding how to design an ndb database - with a one-to-one relationship: for instance, a user and his info. After searching around a lot - found documentation but it was hard to find different examples - and experimenting a bit (modeling and querying in a couple of different ways), this is the solution I found:
from google.appengine.ext import ndb
class Monster(ndb.Model):
name = ndb.StringProperty()
#classmethod
def get_by_name(cls, name):
return cls.query(cls.name == name).get()
def get_info(self):
return Info.query(Info.monster == self.key).get()
class Info(ndb.Model):
monster = ndb.KeyProperty(kind='Monster')
address = ndb.StringProperty()
a = Monster(name = "Dracula")
a.put()
b = Info(monster = a.key, address = "Transilvania")
b.put()
print Monster.get_by_name("Dracula").get_info().address
NDB doesn't accept joins, so the "join" we want has to be emulated using class methods and properties. With the above system I can easily reach a property in the second database (Info) through a unique property in the first (in this case "name" - suppose there are no two monsters with the same name).
However, if I want to print a list with 100 monster names and respective addresses, the second database (Info) will be hit 100 times.
Question: is there a better way to model this to increase performance?
If its truly a one to one relationship, why are creating 2 models. Given your example the Address entity cannot be shared with any Monster so why not put the Address details in the monster.
There are some reasons why you wouldn't.
Address could become large and therefore less efficient to retrieve 100's of properties when you only need a couple - though project queries may help there.
You change your mind and you want to see all monsters that live in Transylvania - in which case you would create the address entity and the Monster would have the key property that points to the Address. This obviously fails when you work out that some monsters can live in multiple places (Werewolfs - London, Transylvania, New York ;-) , in which case you either have a repeating KeyProperty in the monstor or an intermediate entity that points to the monster and the address. In your case I don't think that monsters on the whole have that many documented Addresses ;-)
Also if you are uniquely identifying monsters by name you should consider storing the name as part of the key. Doing a Monster.get_by_id("dracula") is quicker than a query by name.
As I wrote (poorly) in the comment. If 1. above holds and it is a true one to one relationship. I would then create Address as a child entity (Monster is the parent/ancestor in the key) when creating address. This allows you to,
allow other entities to point to the Address,
If you create a bunch of child entities, fetch them with a single
ancestor query). 3 If you have get monster and it's owned entities
again it's an ancestor query.
If you have a bunch of entities that
should only exist if Monster instance exists and they are not
children, then you have to do querys on all the entity types with
KeyProperty's matching the key, and if theses entities are not
PolyModels, then you have to perform a query for each entity
type (and know you need to perform the query on a given entity,
which involves a registry of some type, or hard coding things)
I suspect what you may be trying could be achieved by using elements described in the link below
Have a look at "Operations on Multiple Keys or Entities" "Expando Models" "Model Hooks"
https://developers.google.com/appengine/docs/python/ndb/entities
(This is probably more a comment than an answer)

how to create, syncdb, and access django model dynamically?

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.

Resources