I've been reading a lot about Django and have finally begun developing the models for a project I've outlined, but I'm having trouble understanding ForeignKeys, and how/when to use them. I have read a bit about database work to better understand Foreign keys in general, but with no prior database experience I am still kind of confused.
I've come up with an example situation, similar to the project I'm working on and would like to know if both uses of the ForeignKey are proper, or should I use a OneToOneField or ManyToManyField in their place.
In this example situation, I would like each Bridge to have multiple builders, and each tool to have multiple different users (or Builders who are skilled with them). I would NOT like a builder to work on multiple bridges at the same time (OneToOne??). However, I would like each builder to have the ability to have multiple Tools they are skilled with(ManyToMany??).
class Builder(models.Model):
first_name = models.CharField( max_length = 50 )
class Bridge(models.Model):
bridge_name = models.CharField( max_length = 50 )
builders = models.ForeignKey(Builder)
class Tool(models.Model):
tool_name = models.CharField( max_length = 50 )
users = models.ForeignKey( Builder )
From my understanding, I can retrieve all of a builders' tools in the views.py file, although I haven't gotten to this part of the development yet, and would like to ensure I have a strong model before getting that far.
Considering how generic and open-ended this question can be, overall I would just like someone to explain:
WHY each ForeignKey relationship in this example
model is either used correctly or incorrectly.
It's not actually a Django question but rather a ORM question, you need to understand what and how ORM works (apart from the language and the framework used).
Your case yields different results:
1 Bridge can Have Many workers (One (bridge) <--* Many (Worker), ForeignKey)
1 Bridge can have 1 worker (One (Bridge) <--> One (Worker), One To One field)
Many Bridges can have many workers (Many (Bridges) * -- * many (Workers), Many to Many field)
If you use a ForeignKey from tools to builders, it means that a worker can have many tools, but each tool is unique to a worker, if you have many to many then any worker can have any tool.
If you need tools to belong to many different workers, then you need a many to many relation, if the tools need some specific restrictions (like skills) then again a many to many relation with specific limitations (a through table could hold extra information regarding the association, like worker 1 uses tool 1 every friday, that would be a through table of workers to tools with the middle table holding the day the worker uses the specific tool).
Related
Assuming below (simplified) database schema:
class Site(models.Model):
...
class Business(models.Model):
site = models.ForeignKey(Site)
class Subscription(models.Model):
business = models.ForeignKey(Business)
class Invoice(models.Model):
subscription = models.ForeignKey(Subscription)
Is it better to include site = models.ForeignKey(Site) field in every site-related model (all above) and then ie.:
invoice = Invoice.objects.get(id=1)
invoice_site = invoice.site
or use multi-level relation like this:
invoice = Invoice.objects.get(id=1)
invoice_site = invoice.subscription.business.site
I have such situations in many places of my code and always wonder which option is better. First is cleaner, do not break "DRY" rules and prevents inconsistency but the other allows to gain performance and optimization improvements through preventing complicated database joins.
Of course I assume that site is always consistent in above models, so there is no possibility to have Business with site A and Invoice with site B.
Well, relational database design has no place for DRY, it's all about relations. You need to think logically what the relationship structure of your application should be.
I couldn't speak definitively because I'm not in your shoes to understand the requirement 100%, but I'm almost certain that site shouldn't be directly associated with Invoice. Invoice is related to your business subscriptions and shouldn't depend on/know which site does the business happen. Same thing for Subscription model. A simple trouble if you have site in Invoice is that you are duplicating the relationship, and an Invoice couldn't exist if there's no such site yet.
I suggest you check out some basics of relational database design and gain a better understanding of the concept of normalization. It would help you design a better schema without causing trouble with relations.
I think I need to create a 'many-to-many generic relationship'.
I have two types of Participants:
class MemberParticipant(AbstractParticipant):
class Meta:
app_label = 'participants'
class FriendParticipant(AbstractParticipant):
"""
Abstract participant common information shared for all rewards.
"""
pass
These Participants can have 1 or more rewards of 2 different kinds (rewards model is from another app):
class SingleVoucherReward(AbstractReward):
"""
Single-use coupons are coupon codes that can only be used once
"""
pass
class MultiVoucherReward(AbstractReward):
"""
A multi-use coupon code is a coupon code that can be used unlimited times.
"""
So now I need to link these all up. This is how I was thinking of creating the relationship (see below) would this work, any issues you see?
Proposed linking model below:
class ParticipantReward(models.Model):
participant_content_type = models.ForeignKey(ContentType, editable=False,
related_name='%(app_label)s_%(class)s_as_participant',
)
participant_object_id = models.PositiveIntegerField()
participant = generic.GenericForeignKey('participant_content_type', 'participant_object_id')
reward_content_type = models.ForeignKey(ContentType, editable=False,
related_name='%(app_label)s_%(class)s_as_reward',
)
reward_object_id = models.PositiveIntegerField()
reward = generic.GenericForeignKey('reward_content_type', 'reward_object_id')
Note: I'm using Django 1.6
Your approach is exactly the right way to do it given your existing tables. While there's nothing official (this discussion, involving a core developer in 2007, appears not to have gone anywhere), I did find this blog post which takes the same approach (and offers it in a third-party library), and there's also a popular answer here which is similar, except only one side of the relationship is generic.
I'd say the reason this functionality has never made it into django's trunk is that while it's a rare requirement, it's fairly easy to implement using the existing tools. Also, the chance of wanting a custom "through" table is probably quite high so most end-user implementations are going to involve a bit of custom code anyway.
The only other potentially simpler approach would be to have base Participant and Reward models, with the ManyToMany relationship between those, and then use multi-table inheritance to extend these models as Member/Friend etc.
Ultimately, you'll just need to weigh up the complexity of a generic relation versus that of having your object's data spread across two models.
Late reply, but I found this conversation when looking for a way to implement generic m2m relations and felt my 2 cents would be helpful for future googlers.
As Greg says, the approach you chose is a good way to do it.
However, I would not qualify generic many to many as 'easy to implement using existing tools' when you want to use features such as reverse relations or prefetching.
The 3rd party app django-genericm2m is nice but has several shortcomings in my opinion (the fact that the 'through' objects are all in the same database table by default and that you don't have 'add' / 'remove' methods - and therefore bulk add/remove).
With that in view, because I needed something to implement generic many-to-many relations 'the django way' and also because I wanted to learn a little bit about django internals, I recently released django-gm2m. It has a very similar API to django's built-in GenericForeignKey and ManyToManyField (with prefetching, through models ...) and adds deletion behavior customisation. The only thing it lacks for the moment is a suitable django admin interface.
I'm building my first Django app to manage multiple SaaS products.
This entails storing custom attributes for each Version of each Product.
For example, a new version of a Product is released that includes new configuration options that the earlier versions of the Product do not support.
I need to be able to keep track of those new values for each instance of the new Version.
I'm thinking I want the Admins to be able to add "custom fields" at the Product level by Version.
Looking for suggestions as to the best approach.
Thanks.
The common way of tracking model versions is to use django-reversion.
It sounds like each instance needs its own custom attributes. That means that changing the Models relating to Product and Version need not occur. This is good, because models can only change with the code (unless you get into dynamically generating Models which is usually not a good idea).
So, you need to be able to model attributes for each Product instance, regardless of Version. This should be a simple data modelling exercise, not necessarily related to Django.
A Product has a set of fields
A Product has a Version
A Product has a set of Attributes
This is quite easily modelled, depending on how you want to manage attributes.
class Version(models.Model):
version = models.CharField(max_length=10)
class ProductAttributes(models.Model):
name = models.CharField(max_length=64)
description = models.CharField(max_length=255)
# other fields as necessary
class Product(models.Model):
name = models.CharField(max_length=64)
version = models.ForeignKey(Version)
attributes = models.ManyToManyField(ProductAttributes, related_name='products')
That should be your modelling sorted in a very basic way. Now, let's create some instances.
v1 = Version(version='1.0.0')
v1.save()
hosted = ProductAttributes(name='Hosted', description='We host the apps!')
hosted.save()
shiny = ProductAttributes(name='Shiny', description='I like shiny')
shiny.save()
p = Product(name='Web Based Email', version=v1)
p.save()
p.attributes.add(hosted)
p.attributes.add(shiny)
p.attributes.all()
# shows shiny and hosted!
You can tweak the ModelAdmin for Product such that you can add ProductAttributes inline when adding or editing a Product. You can also have a separate ModelAdmin for ProductAttributes so you can create a list of known Attributes that can be applied to products at a later date.
There are two basic approaches for this.
Use a document based db (ie, "NoSQL") like Couch or Mongo. These have flexible schemas, so allow for multiple variations on a product.
Use the Entity Attribute Value (wikipedia) schema pattern. django-eav is an app that provides this.
Decide to go with sub-classes with each Product as each has a limited set of specific attributes that won't change much or at all over time. Thanks for all the great feedback. Learned a lot :-)
Half way through my current project, after suffering the pain of spending uncountable minutes on debugging, I have decided to adopt TDD. To start, I am planning to write a set of unit tests for each existing models. But for models that only have attributes defined (ie. no additional methods/properties) I am not sure what I need to test nor how.
class Product(models.Model):
name = models.CharField(max_length=50)
description = models.TextField(default='', blank=True)
retails = models.ManyToManyField(Retail, verbose_name='Retail stores that carry the product')
manufacturer = models.ForeignKey(Manufacturer, related_name='products')
date_created = models.DateTimeField(auto_now_add=True)
date_modified = models.DateTimeField(auto_now=True)
Using Product as an example, what are the things about it that unit tests should cover? And how should ForeignKey and ManyToManyField be covered?
This was an article I found helpful: A Guide to Testing in Django (archived link). Here is a good summary of what to test:
Another common setback for developers/designers new to testing is the
question of 'what should (or shouldn't) I test?' While there are no
hard & fast rules here that neatly apply everywhere, there are some
general guidelines I can offer on making the decision:
If the code in question is a built-in Python function/library, don't test it. Examples like the datetime library.
If the code in question is built into Django, don't test it. Examples like the fields on a Model or testing how the built-in
template.Node renders included tags.
If your model has custom methods, you should test that, usually with unit tests.
Same goes for custom views, forms, template tags, context processors, middleware, management commands, etc. If you implemented
the business logic, you should test your aspects of the code.
So, for your example, there wouldn't really be anything to test until you write some custom functions.
In my opinion, testing ForeignKey and ManyToManyField links would fall under the second category (code built into Django), so I wouldn't test these, as you are really testing whether or not Django is functioning properly. If you have a method which creates an instance of your product, including foreign relationships and M2Ms, you could verify the data has been created, that would be testing your custom method, not Django functionality.
Using the TDD paradigm, the tests are built to verify business logic, and design requirements.
My CS350 class TDD stipulated that it's best practice to test all accessors and mutators. So for a model, you would first write tests that call each assessor function and make sure that it returns the proper value.
For each function which changes a data field in the model, you would not only test the result of that data field in particular, but you would also test all of the other fields in the model instance to make sure that none of them were modified erroneously.
To restat:, if a model has fields a, b, and c, you would create an instance using your constructor, then asset that all three are set properly. Say there's another function, set_a(). You would assert that not only the value of 'a' has changed, but that the values of b and c remain unchanged.
I'd like to ask about the most elegant approach when it comes to designing models with virtual fields such as below in Django...
Let's say we're building an online store and all the products in the system are defined by the model "Product".
class Product(models.Model):
# common fields that all products share
name = ...
brand = ...
price = ...
But the store will have lots of product types completely unrelated with eachother, so I need some way to store those virtual fields of different product types (ie. capacity of a MP3 player, pagecount of a book,..).
The solutions I could come up with my raw Django skills are far from perfect so far:
Having a "custom_fields" property
and intermediate tables that I
manage manually. (screaming ugly in
my face :))
Or inheriting classes from
"Product" on the fly with Python's
dangerous exec-eval statements (that is too
much voodoo magic for maintenance
and also implementation would
require knowledge of Django internals).
What's your take on this?
TIA.
Products have Features.
class Feature( models.Model ):
feature_name = models.CharField( max_length=128 )
feature_value = models.TextField()
part_of = models.ForeignKey( Product )
Like that.
Just a list of features.
p= Product( "iPhone", "Apple", 350 )
p.save()
f= Feature( "mp3 capacity", "16Gb", p )
f.save()
If you want, you can have a master list of feature names in a separate table. Don't over-analyze features. You can't do any processing on them. All you do is present them.
Ruby on Rails has a "serialized" field which allows you to pack a dictionary into a text field. Perhaps DJango offers something similar?
This article has an implementation of a SerializedDataField.
Personally, I'd go with S. Lott's answer. However, you might want to create a custom JSON Field:
http://svn.navi.cx/misc/trunk/djblets/djblets/util/fields.py
http://www.djangosnippets.org/snippets/377/
Go with the inheritance. Create Produce subclasses with their own, additional fields.