Models/Foreign Keys best practice

65 views
Skip to first unread message

Richard Jackson

unread,
Feb 22, 2017, 9:25:24 AM2/22/17
to Django users
Hi all,

I'm putting together my first app, closely modelled on the Django tutorial, and wanted some advice regarding when to split the various models into different foreign keys.

The app lists performances, who's performing, what my role is with them (i.e. conductor, pianist etc.), the venue and the start date/time of these performances.

I'd initially thought I should split these all into separate classes, since, for example, the same Ensemble might be involved in a different performance, but I'm now not sure that's correct (/my understanding is incorrect). Would someone mind letting me know if this is a good idea or whether there's a better practice I should follow.

I've included my models.py file below:

from django.db import models
from django.utils import timezone
# Create your models here.


class Performance(models.Model):
    """First attempt at a thing!"""
    performance_name = models.CharField(max_length=200)
    link = models.CharField(max_length=200)

    def __unicode__(self):
        return self.performance_name


class Ensemble(models.Model):
    performance = models.ForeignKey(Performance)
    ensemble = models.CharField(max_length=200)

    def __unicode__(self):
        return self.ensemble


class Role(models.Model):
    performance = models.ForeignKey(Performance)
    role = models.CharField(max_length=200)

    def __unicode__(self):
        return self.role


class Venue(models.Model):
    performance = models.ForeignKey(Performance)
    venue = models.CharField(max_length=200)

    def __unicode__(self):
        return self.venue


class Date(models.Model):
    performance = models.ForeignKey(Performance)
    start_date = models.DateTimeField('Start Date', default=timezone.now)
    end_date = models.DateTimeField('End Date', default=timezone.now)


Thanks!

Rich

Luvpreet Singh

unread,
Feb 22, 2017, 10:30:06 AM2/22/17
to Django users
Hi Richard,

I am facing a difficulty here.
Can you please tell me that how to iterate over this queryset , performance.ensemble_set.all()  ??

Thanks.

Melvyn Sopacua

unread,
Feb 22, 2017, 12:01:36 PM2/22/17
to django...@googlegroups.com

Hi Richard,

 

good questions and your initial data model defines your application challenges. It is by far the most important step in application design, so it's important to get it "as right as possible".

 

On Wednesday 22 February 2017 06:25:24 Richard Jackson wrote:

 

> The app lists performances, who's performing, what my role is with

> them (i.e. conductor, pianist etc.), the venue and the start

> date/time of these performances.

 

I come up with these separate entities:

- Performance (or "Show" or "Series")

- Ensemble

- Role within Ensemble

- Venue

- Event: performance using a certain Ensemble, playing a Role at a certain date

 

Date is a bad class name to use for a variety of reasons, but also doesn't cover the load. Event does.

 

Now you can see the links clearly:

- Role: Ensemble

- Event: Ensemble, Role, Performance

 

The questions to answer is: Within each ensemble can you switch roles? If so, they don't need to be linked and Event will link them

 

In fact, it's probably best not to link them, as you can always enforce this in logic.

 

A more general remark:

While you're free to use the class name as the field name, most of us will use "name", "title" or "designation" if you're Borg minded, for the field name. So Role.role becomes Role.name.

 

Results:

 

> class Performance(models.Model):

> """First attempt at a thing!"""

> performance_name = models.CharField(max_length=200)

> link = models.CharField(max_length=200)

>

> def __unicode__(self):

> return self.performance_name

>

>

> class Ensemble(models.Model):

> performance = models.ForeignKey(Performance)

 

No key here

 

> ensemble = models.CharField(max_length=200)

>

> def __unicode__(self):

> return self.ensemble

>

>

> class Role(models.Model):

> performance = models.ForeignKey(Performance)

 

No key here

 

> role = models.CharField(max_length=200)

>

> def __unicode__(self):

> return self.role

>

>

> class Venue(models.Model):

> performance = models.ForeignKey(Performance)

 

No key here. This is just the place. It should be possible to do two different performances at the same venue, without having to create two venues.

 

> venue = models.CharField(max_length=200)

>

> def __unicode__(self):

> return self.venue

>

>

> class Date(models.Model):

 

Event

 

> performance = models.ForeignKey(Performance)

ensemble = models.ForeignKey(Ensemble)

role = models.ForeignKey(Role)

venue = models.ForeignKey(Venue)

 

> start_date = models.DateTimeField('Start Date',

> default=timezone.now)

 

I would use "start" or start_time since it's not just a date.

 

> end_date = models.DateTimeField('End Date',

> default=timezone.now)

 

I would use duration = models.DurationField()

 

 

Now, if you look at "Event", it contains foreign keys to models that only have a charfield. So, it makes sense to simply make those CharFields.

But...a Venue, has an address. An Ensemble has members and you may get different but fixed wages for each role. These are reasons to use Foreign Keys. Otherwise, just use a CharField.

 

If you don't need to record anything special about roles, there's also the middle ground using a field's choices. You use choices instead of a Foreign Key if they hardly ever change and don't need to be managed in the Admin.

 

Hope this helps.

--

Melvyn Sopacua

Richard Jackson

unread,
Feb 22, 2017, 1:38:26 PM2/22/17
to Django users
Hi Melvyn - great reply, thank you so much for taking the time to help! I've got to head out now but will reply tomorrow - just wanted to leave an initial thank you note!

Rich

Richard Jackson

unread,
Feb 24, 2017, 6:42:59 PM2/24/17
to Django users
Hi Melvyn,

This worked perfectly, and definitely helped with my own understanding, thank you!

For the sake of formatting I've included my interpretation of your suggestion:

# models.py
from django.db import models
from django.utils import timezone


class Performance(models.Model):
    performance_name = models.CharField(max_length=200)
    link = models.CharField(max_length=200)

    def __unicode__(self):
        return self.performance_name


class Ensemble(models.Model):
    ensemble = models.CharField(max_length=200)

    def __unicode__(self):
        return self.ensemble


class Role(models.Model):
    role = models.CharField(max_length=200)

    def __unicode__(self):
        return self.role


class Venue(models.Model):
    venue = models.CharField(max_length=200)

    def __unicode__(self):
        return self.venue


class Event(models.Model):  # previously called "Date"
    performance = models.ForeignKey(Performance)
    ensemble = models.ForeignKey(Ensemble)
    role = models.ForeignKey(Role)
    venue = models.ForeignKey(Venue)
    start_datetime = models.DateTimeField('Start Date', default=timezone.now)
    end_datetime = models.DateTimeField('End Date', default=timezone.now)

Thanks again!

Rich
Reply all
Reply to author
Forward
0 new messages