many-to-many relation with extra fields - how to circumvent uniqueness - django

I have the following task.
Having three models Project, User and PurchaseOrder. I want to be able to create a membership for a User in a Project. A User can be a member in arbitrary Projects. This can be solved with a
ManyToManyField.
Additionally a membership should reference to a PurchaseOrder, since I want to assign the working hours to specific PurchaseOrders.
I think this could be solved by using a through-table for the ManyToManyField, and defining a ForeignKey to the PurchaseOrder model. Thus I would have for each membership a reference to a PurchaseOrder.
In reality a membership for a project will stay active, whereas after the money is spend for a PurchaseOrder, a new PurchaseOrder has to be assigned to the membership. This would also be easy by just updating the ForeignKey to a new PurchaseOrder.
But now my question:
I want to keep the old Project-membership-PurchaseOrder-Relation (data row in the membership table, for history tracking), set it to disabled and add a new Project-membership-PurchaseOrder-Relation, which would have the same ForeignKey to User and Project, but a different to the PurchaseOrder, and a flag set to enabled.
Is this a valid approach, will this work, will it be possible to circumvent uniqueness (or is there no uniqueness for the ManyToManyField by definition), or do you have a better idea how to do this?

When I read through this I can't figure out why even bother with the Many-to-Many relation for the Member > PurchaseOrder.
I would make it a One-to-Many relation for Member > PurchaseOrder and a Many-to-Many relation Member > Project, as the Membership appears to be the primarykey for it all.
In that way, you don't have to update any keys. Then I would create a fourth Model, having a Many-to-Many relation keeping track of the purchases. Adding the Membership prim. key and the PurchaseOrder prim.key.

Related

Can I create a related Django field between two models with a shared foreign key value?

I have two models:
class Foo(Model):
special_id = IntegerField(primary_key=True)
class FooDetail(Model):
special_id = IntegerField(primary_key=True)
special_id comes from an outside source -- it's a foreign key into another database. Yes, Foo and FooDetail should be combined into a single model -- but assuming I can't -- can I create a related field between the two models such that I can use it in queries (like in values or select_related)?
I could add a ForeignKey('FooDetail') in Foo, but I'd be essentially storing the special_id twice.
If you want to use the ORM's features for related models, you should create a relationship (one-to-one in this case) between the two models. In one of the models you can (and should) then omit the special_id reference.
You can use the foreign key as a primary key in FooDetail, and if you keep special_id as a primary key in Foo, you'll be saving exactly the same type and amount of columns and data as in your example (namely one column in each that contains the relevant special_id).
What you get though is the benefit of a relationship and enforced integrity.
The only difference is that when you introduce a new special_id, you have to create Foo first to be able to point to it in FooDetail – hardly a big price to pay.
If you get a warning on setting the reference field to Foo to be the primary key then it might be that you defined it as a ForeignKey. You should define the field as a OneToOneField since you're dealing with a one-to-one relationship as noted above. The field is still technically a foreign key (= reference to the primary key of a row in another table) which is why I used this term; but it has a unique constraint that allows it to be used as a primary key.

Can I make a dynamc number of foreign keys to a single (self) django model?

I'm currently creating an equipment management database and need to allow equipment to be associated with other equipment.
Thanks to this stackoverflow question I currently have something akin to the following (vastly simplified):
class Equipment(models.Model):
equipment_title = models.CharField()
relates_to = models.ForeignKey('self')
However, to relate a dynamic number of equipment to other equipment I think I need something like a one-to-many field that doesn't exist natively within Django, e.g. a filter housing may be associated with many filter units, and several filter housings may be associated with a machine tool.
How can I get around this? I'm not sure that it's the right place for a many-to-many field...
A ForeignKey is a one-to-many relationship, defined on the "many" side. Since your relationship is pointing to self anyway, it already does what you want.

Do Django models really need a single unique key field

Some of my models are only unique in a combination of keys. I don't want to use an auto-numbering id as the identifier as subsets of the data will be exported to other systems (such as spreadsheets), modified and then used to update the master database.
Here's an example:
class Statement(models.Model):
supplier = models.ForeignKey(Supplier)
total = models.DecimalField("statement total", max_digits=10, decimal_places=2)
statement_date = models.DateField("statement date")
....
class Invoice(models.Model):
supplier = models.ForeignKey(Supplier)
amount = models.DecimalField("invoice total", max_digits=10, decimal_places=2)
invoice_date = models.DateField("date of invoice")
statement = models.ForeignKey(Statement, blank=True, null=True)
....
Invoice records are only unique for a combination of supplier, amount and invoice_date
I'm wondering if I should create a slug for Invoice based on supplier, amount and invoice_date so that it is easy to identify the correct record.
An example of the problem of having multiple related fields to identify the right record is django-csvimport which assumes there is only one related field and will not discriminate on two when building the foreign key links.
Yet the slug seems a clumsy option and needs some kind of management to rebuild the slugs after adding records in bulk.
I'm thinking this must be a common problem and maybe there's a best practice design pattern out there somewhere.
I am using PostgreSQL in case anyone has a database solution. Although I'd prefer to avoid that if possible, I can see that it might be the way to build my slug if that's the way to go, perhaps with trigger functions. That just feels a bit like hidden functionality though, and may cause a headache for setting up on a different server.
UPDATE - after reading initial replies
My application requires that data may be exported, modified remotely, and merged back into the master database after review and approval. Hidden autonumber keys don't easily survive that consistently. The relation invoices[2417] is part of statements[265] is not persistent if the statement table was emptied and reloaded from a CSV.
If I use the numeric autonumber pk then any process that is updating the database would need to refresh the related key numbers or by using the multiple WITH clause.
If I create a slug that is based on my 3 keys but easy to reproduce then I can use it as the key - albeit clumsily. I'm thinking of a slug along the lines:
u'%s %s %s' % (self.supplier,
self.statement_date.strftime("%Y-%m-%d"),
self.total)
This seems quite clumsy and not very DRY as I expect I may have to recreate the slug elsewhere duplicating the algorithm (maybe in an Excel formula, or an Access query)
I thought there must be a better way I'm missing but it looks like yuvi's reply means there should be, and there will be, but not yet :-(
What you're talking about it a multi-column primary key, otherwise known as "composite" or "compound" keys. Support in django for composite keys today is still in the works, you can read about it here:
Currently Django models only support a single column in this set,
denying many designs where the natural primary key of a table is
multiple columns [...] Current state is that the issue is
accepted/assigned and being worked on [...]
The link also mentions a partial implementation which is django-compositekeys. It's only partial and will cause you trouble with navigating between relationships:
support for composite keys is missing in ForeignKey and
RelatedManager. As a consequence, it isn't possible to navigate
relationships from models that have a composite primary key.
So currently it isn't entirely supported, but will be in the future. Regarding your own project, you can make of that what you will, though my own suggestion is to stick with the fully supported default of a hidden auto-incremented field that you don't even need to think about (and use unique_together to enforce the uniqness of the described fields instead of making them your primary keys).
I hope this helps!
No.
Model needs to have one field that is primary_key = True. By default this is the (hidden) autofield which stores object Id. But you can set primary_key to True at any other field.
I've done this in cases, Where i'm creating django project upon tables which were previously created manually or through some other frameworks/systems.
In reality - you can use whatever means you can think of, for joining objects together in queries. As long as query returns bunch of data that can be associated with models you have - it does not really matter which field you are using for joins. Just keep in mind, that the solution you use should be as effective as possible.
Alan

Zend2 Doctrine2 One-To-Many uni-directional with join table, delete cascade issue

I'm having some problems with the following...
I have a table with phone numbers. I want to use the same table for both users and companies. A user can have several phone numbers and a company too. So i want a One to many unidirectional relationship with two different join tables. One linking phone numbers to users, the other linking phone numbers to companies.
This is solution following the doctrine2 manual chapter 5.9 found here: (click)
My users entity holds this code:
/** #ORM\ManyToMany(targetEntity="Application\Entity\PhoneNumber")
* #ORM\JoinTable(name="user_phone_number_linker",
* joinColumns={#ORM\JoinColumn(name="user_id", referencedColumnName="id")},
* inverseJoinColumns={#ORM\JoinColumn(name="phone_number_id", referencedColumnName="id")}
* )
*/
protected $phone_numbers;
I use a unidirectional one to many because the thing is I can't make a bidirectional one because if I refer back to the user I cannot use the same phone number entity class for the company. Now it all works fine, but when I delete a phone number I get the following error:
An exception occurred while executing 'DELETE FROM phone_number WHERE id = ?' with params {"1":1}:
SQLSTATE[23000]: Integrity constraint violation: 1451 Cannot delete or update a parent row: a foreign key constraint fails (database/user_phone_number_linker, CONSTRAINT user_phone_number_linker_ibfk_11 FOREIGN KEY (phone_number_id) REFERENCES phone_number (id))
If I set the ON DELETE CASCADE value manually in the database it works fine, but this is not the idea of using doctrine2 and I think I should be able to solve it within the code without going to my phpMyAdmin panel. Somehow the cascading from the phone number towards the join table should be initiated on deletion, but without making a reference back to the join table from the phone_number entity.
Hope someone smart can help me solve this.
EDIT
In the meantime I learned a lot more about Doctrine2 and reviewing my old question made me realize that this is not a correct way to store several phoneNumbers in one table in the first place. To be able to store user phone numbers and company phone numbers in the same table I should use table inheritance with a discriminator column. The column should hold some user/company discriminator.
Because of this column the doctrine ORM will "know" if that phoneNumber is actually a user or a company phone-number. I need to make two different entity definitions following the single table inheritance mapping principles from the doctrine 2 specs.
One class UserPhoneNumber will have a many-to-one relationship with User the other called CompanyPhoneNumber a one-to-many relationship with Company. I don't necessarily need a join column, the user_id or company_id columns can be in the phone-number table. In the User class the Company association is omitted and in the Company class the User association is omitted (database should allow null values for those columns).
If I do use a join table it is according to the one-to-many unidirectional with join table description in the Doctrine2 specs
READ MORE
Otherwise you can also read more on associations and cascade issues here on this elaborate Doctrine2 in depth website.
As you said, your relation is unidirectional. You've defined a relation from Users to PhoneNumbers. The cascade delete will work when you delete a User, it will remove all rows in user_phone_number_linker because that's the relation you've defined.
If you want to do it the other way, you've got to create a relation from PhoneNumbers to Users. Doctrine needs it to work for you. But you have the problem that the entity is shared by two other entities, Users and Companies.
Keep in mind that entities are objects, not tables. So you could try to create two entities to the same table, one named PhoneNumberUsers and the other PhoneNumberCompanies. This way you'll be able to create the needed relation to do the cascade delete. I haven't tested by myself, but I think it could work.
By the way, you can remove the oncascade parameter on the Users' entity join table. I've the same scenario as you with users and roles, and I haven't used it. I think it's only needed when you want to cascade from entity to entity. I'm not sure about that, but that's what I've been experiencing until now.
My bad,
The phone number user relationship is regarded a Many-To-Many relationship, so if want to remover the phone number I should not only remove the phone number itself, but I have to explicitly remove the phone number from the user as well. So in the Controller like this:
// Remove the phone number user connection from the database
$user->removePhoneNumber($phone_number);
// Remove the phone number from the database
$em->remove($phone_number);
I just thought the unique restriction which makes the relationship to a unidirectional One-To-Many would be enough to make doctrine take care of it. That was not correct.

How do I express a Django ManyToMany relationship?

I'm hitting a wall here and I know this is a simple question, but I was unable to find it here.
In an ER diagram, what would the relationship be between two objects that have a ManyToMany relationship, in terms of the intermediary table?
Example:
item ---- item_facts ---- fact
I feel like it should be one to one but I'm not completely sure.
user --many2many-- group
user 1----n user_group n---1 group
In django documentation it states that
A many-to-many relationship. Requires a positional argument: the class to which the model is related. This works exactly the same as it does for ForeignKey, including all the options regarding recursive and lazy relationships.
Behind the scenes, Django creates an intermediary join table to represent the many-to-many relationship. By default, this table name is generated using the name of the many-to-many field and the model that contains it. Since some databases don't support table names above a certain length, these table names will be automatically truncated to 64 characters and a uniqueness hash will be used. This means you might see table names like author_books_9cdf4; this is perfectly normal. You can manually provide the name of the join table using the db_table option.
And ForeignKey definition is like:
A many-to-one relationship. Requires a positional argument: the class to which the model is related.
So,ManyToMany relations created by django are creating intermedıary tables that are 1 to N.
Not sure what the question is here. You say that the two objects have a many-to-many relationship.
If two objects (entitied, tables) have a many-to-many relationship, whether you include the intermediate table in the diagram or not, is irrelevant. They still have a many-to-many relationship.