Help with defining Models for ManyToMany and OneToMany relationships...

91 views
Skip to first unread message

Bruce Whealton

unread,
Apr 23, 2016, 10:30:45 AM4/23/16
to Django users
Hello all,
          So, I setup django in a virtualenv on my Ubuntu environment.  I was reading the docs and thought I had things right 
for creating the 3 models I wanted with this application. I am using Postgresql.  I have the Postgresql driver for Python/Django installed
in the virtualenv.  It is a "Contacts" app.  
First question: Do django model fields default to required unless you use blank=True, null=True?
Many of my fields, I want to have optional.

I have a class called Contact, a class called Organization and a class called Connection.  
I wanted to use the Organization as a foreign key on the Contact model.  I could have more than one contact from
an Organization.  The Connection model is inspired by the Google Plus idea of "Circles" - e.g. friends,
family, following, etc.   So, this would be a many-to-many relationship.  

My problems are (1) I cannot create connections without specifying a contact.  
(2) If I was adding a contact using the admin interface, how do I allow no value for that foreign field
or allow for some kind of ajax type of text completion?  If a person is family or friend, I may not need
to list an Organization for them.
(3) I would like to support multiple connection types - e.g. following, employer, etc.

So, here is my apps models.py file:
>>>>

from django.db import models


class Contact(models.Model):
    name = models.CharField(max_length=40)
    Organization = models.CharField(max_length=50)
    street_line1 = models.CharField("Street Line 1", max_length=50)
    street_line2 = models.CharField("Street Line 2", max_length=50)
    city = models.CharField(max_length=40)
    state = models.CharField(max_length=40)
    zipcode = models.CharField(max_length=20, blank=True, null=True)
    phone1 = models.CharField(max_length=20)
    phone2 = models.CharField(max_length=20)
    email = models.EmailField(max_length=60)


class Organization(models.Model):
    name = models.CharField(max_length=60)
    street_line1 = models.CharField("Street Line 1", max_length=50)
    street_line2 = models.CharField("Street Line 2", max_length=50)
    city = models.CharField(max_length=40)
    state = models.CharField(max_length=40)
    zipcode = models.CharField(max_length=20, blank=True, null=True,)
    phone = models.CharField(max_length=20)
    email = models.EmailField(max_length=60)
    website = models.URLField(max_length=90)
    contact_name = models.ForeignKey(Contact, on_delete=models.CASCADE)


class Connection(models.Model):
    type = models.CharField(max_length=60)
    contact_name = models.ManyToManyField(Contact)   

>>>
Thanks in advance for any suggestions,
Bruce 

Camilo Torres

unread,
Apr 23, 2016, 9:01:16 PM4/23/16
to Django users
Hi,
1.  Do django model fields default to required unless you use blank=True, null=True?

2. I think you need a foreign key on Contact to Organization:

class Contact(models.Model):
    name = ....
    ....
    organization = models.ForeignKey('Organization', null=True, blank=True)

That way you can have many contacts to a single organization. The contact_name in Organizations allows only 1 Contact, but you said you need many.

3. I cannot create connections without specifying a contact.
I don't see why not. Do you get any error?

4. If I was adding a contact using the admin interface, how do I allow no value for that foreign field
By setting null=True, blank=True for the ForeignKey field. Notice Contact do not have any ForeignKey in your example.

5. or allow for some kind of ajax type of text completion?

6.  I would like to support multiple connection types - e.g. following, employer, etc.
You already have a 'type' field in Connections model. First I suggest to use a different name for the field: type is a reserved Python word.
Second, you can add choices to the field.

from django.utils.translation import ugettext as _
...
class Connection(models.Model):
    CHOICES = (('follower', _('Follower')),
               ('employer', _('Employer')),
               ('unspecified', _('Unspecified')),
              )
    connection_type = models.CharField(max_length=20, choices=CHOICES)
    contact_name = models.ManyToManyField(Contact)  



Mike Dewhirst

unread,
Apr 24, 2016, 1:13:16 AM4/24/16
to django...@googlegroups.com
On 23/04/2016 11:22 PM, Bruce Whealton wrote:
> Hello all,
> Â Â Â Â Â So, I setup django in a virtualenv on my Ubuntu
> environment. Â I was reading the docs and thought I had things rightÂ
> for creating the 3 models I wanted with this application. I am using
> Postgresql. Â I have the Postgresql driver for Python/Django installed
> in the virtualenv. Â It is a "Contacts" app. Â
> First question: Do django model fields default to required unless you
> use blank=True, null=True?
> Many of my fields, I want to have optional.
>
> I have a class called Contact, a class called Organization and a class
> called Connection. Â

I think you should rethink your Contact and Organization classes and see
if you can eliminate one or the other. A single table for both would
simplify the problem because the Connection class can implement as many
connections as you like.

For example ...

class ContactOrOrganization(etc):
various detail fields ...

class Connection(etc):
organization = ForeignKey("ContactOrOrganization",
related_name="organization")
contact = ForeignKey("ContactOrOrganization",
related_name="contact")

Just because I used related_name that way means nothing. You can connect
contacts together or organizations together. Also, you can add other
fields to Connection with which to describe the relationship.

Mike

> I wanted to use the Organization as a foreign key on the Contact model.
> Â I could have more than one contact from
> an Organization. Â The Connection model is inspired by the Google Plus
> idea of "Circles" - e.g. friends,
> family, following, etc. Â So, this would be a many-to-many relationship. Â
>
> My problems are (1) I cannot create connections without specifying a
> contact. Â
> (2) If I was adding a contact using the admin interface, how do I allow
> no value for that foreign field
> or allow for some kind of ajax type of text completion? Â If a person is
> family or friend, I may not need
> to list an Organization for them.
> (3) I would like to support multiple connection types - e.g. following,
> employer, etc.
>
> So, here is my apps models.py file:
> >>>>
>
> from django.db import models
>
>
> class Contact(models.Model):
> Â Â name = models.CharField(max_length=40)
> Â Â Organization = models.CharField(max_length=50)
> Â Â street_line1 = models.CharField("Street Line 1", max_length=50)
> Â Â street_line2 = models.CharField("Street Line 2", max_length=50)
> Â Â city = models.CharField(max_length=40)
> Â Â state = models.CharField(max_length=40)
> Â Â zipcode = models.CharField(max_length=20, blank=True, null=True)
> Â Â phone1 = models.CharField(max_length=20)
> Â Â phone2 = models.CharField(max_length=20)
> Â Â email = models.EmailField(max_length=60)
>
>
> class Organization(models.Model):
> Â Â name = models.CharField(max_length=60)
> Â Â street_line1 = models.CharField("Street Line 1", max_length=50)
> Â Â street_line2 = models.CharField("Street Line 2", max_length=50)
> Â Â city = models.CharField(max_length=40)
> Â Â state = models.CharField(max_length=40)
> Â Â zipcode = models.CharField(max_length=20, blank=True, null=True,)
> Â Â phone = models.CharField(max_length=20)
> Â Â email = models.EmailField(max_length=60)
> Â Â website = models.URLField(max_length=90)
> Â Â contact_name = models.ForeignKey(Contact, on_delete=models.CASCADE)
>
>
> class Connection(models.Model):
> Â Â type = models.CharField(max_length=60)
> Â Â contact_name = models.ManyToManyField(Contact) Â Â
>
> >>>
> Thanks in advance for any suggestions,
> BruceÂ
>
> --
> You received this message because you are subscribed to the Google
> Groups "Django users" group.
> To unsubscribe from this group and stop receiving emails from it, send
> an email to django-users...@googlegroups.com
> <mailto:django-users...@googlegroups.com>.
> To post to this group, send email to django...@googlegroups.com
> <mailto:django...@googlegroups.com>.
> Visit this group at https://groups.google.com/group/django-users.
> To view this discussion on the web visit
> https://groups.google.com/d/msgid/django-users/caede40b-640c-4e8d-997d-b76c62922c19%40googlegroups.com
> <https://groups.google.com/d/msgid/django-users/caede40b-640c-4e8d-997d-b76c62922c19%40googlegroups.com?utm_medium=email&utm_source=footer>.
> For more options, visit https://groups.google.com/d/optout.

Bruce Whealton

unread,
Apr 26, 2016, 6:07:56 AM4/26/16
to Django users
OK, for starters, I understand the need for Null=True and blank=True.  So, jumping down...Not sure a better way to do this
but I will leave my comments for reference including my models and then respond to the questions....
So sorry for my delay, I wasn't well this past weekend.

Hi,
1.  Do django model fields default to required unless you use blank=True, null=True?

2. I think you need a foreign key on Contact to Organization:

class Contact(models.Model):
    name = ....
    ....
    organization = models.ForeignKey('Organization', null=True, blank=True)

That way you can have many contacts to a single organization. The contact_name in Organizations allows only 1 Contact, but you said you need many.
>>> 
Ok that makes sense, I need a foreign key on the Contact table to link to the Organization.  I am not sure how easy it will be to show the results both ways 
that is ask for Contacts and see Orgs, or ask for Orgs and see all Contacts with the Org.  

3. I cannot create connections without specifying a contact.
I don't see why not. Do you get any error?

This is something I thought people might say, "why do you want to do this?"  On Google Circles, you don't create circles typically and insert people into them.
Anyway, It seems to require that I pick a contact when I am creating a Connection.  Note, I used "connection" as a similar term to the way Google Plus uses "Circles" 
as that app seems to work well for me.  I will try again and see if it lets me save the connection without listing a contact. 
 
4. If I was adding a contact using the admin interface, how do I allow no value for that foreign field
By setting null=True, blank=True for the ForeignKey field. Notice Contact do not have any ForeignKey in your example.
>>>
As you stated, I do need to have a ForeignKey within Contacts probably for both Organizations and Connection tables. 

5. or allow for some kind of ajax type of text completion?

6.  I would like to support multiple connection types - e.g. following, employer, etc.
You already have a 'type' field in Connections model. First I suggest to use a different name for the field: type is a reserved Python word.
Second, you can add choices to the field.

from django.utils.translation import ugettext as _
...
class Connection(models.Model):
    CHOICES = (('follower', _('Follower')),
               ('employer', _('Employer')),
               ('unspecified', _('Unspecified')),
              )
    connection_type = models.CharField(max_length=20, choices=CHOICES)
    contact_name = models.ManyToManyField(Contact)  

OK, that sounds great.  I debated over using choices but I can do a migrate at any time to get the Choices to allow 
more options... which isn't a difficult migration as nothing is changing about the schema per se, just the values allowed in
Choices.
Thanks,
Bruce 

Bruce Whealton

unread,
Apr 26, 2016, 7:41:33 AM4/26/16
to Django users
Mike,
         So, I tried your idea for reorganizing the models, and just removed Organization and instead setup
ContactOrOrganization as a class.  
It seemed to work ok, in terms of migrating fine.  However, the database now lacks a ContactsOrOrganization 
table.  
Oops, my mistake, it does have a table now for that model.  I wonder if I need a ForeignKey field in the ContactsOrOrganization table?
Bruce


Anyway, I will try to remove the ManyToMany statement from the Connections Model.  
I would then have one Contact or Organization maps to many Connection types.  

Bruce Whealton

unread,
Apr 26, 2016, 8:38:26 AM4/26/16
to Django users
Having followed all the suggestions, I am now stuck.  I have simplified the database by removing one model.  I get to the point of 
python manage.py makemigrations and it returns with what looks right.  However, the migrate command just throws out a bunch of errors.  For some reason,
it is looking at the choices variable field and telling me that 'following' is not an integer field.  From the directions I read, in similar posts, it seemed like too many parentheses were used for the 
CHOICES.  

Here is what I have now.  I hope this is helpful in getting this to work.
See below:

from django.db import models


class ContactOrOrganization(models.Model):
    name = models.CharField(max_length=40)
    organization = models.CharField(max_length=60, null=True, blank=True)
    street_line1 = models.CharField("Street Line 1", max_length=50, null=True, blank=True)
    street_line2 = models.CharField("Street Line 2", max_length=50, null=True, blank=True)
    city = models.CharField(max_length=40, null=True, blank=True)
    state = models.CharField(max_length=40, null=True, blank=True)
    zipcode = models.CharField(max_length=20, blank=True, null=True)
    phone1 = models.CharField(max_length=20, null=True, blank=True)
    phone2 = models.CharField(max_length=20, null=True, blank=True)
    email = models.EmailField(max_length=60, null=True, blank=True)
    website = models.URLField(max_length=90, null=True, blank=True)
    connections = models.ForeignKey('Connection', on_delete=models.CASCADE)


class Connection(models.Model):
    CHOICES = (('following', 'Following'),
                ('family', 'Family'),
                ('friend', 'Friend'),
                ('clients', 'Clients'),
                ('recruiters', 'Recruiters'),
                ('acquaintances', 'Acquaintances'),
                ('employers', 'Employers'),
                ('Employment_Agencies', 'Employment Agencies'),
                ('unspecified', 'Unspecified'),
    )
    organization = models.ForeignKey("ContactOrOrganization", related_name="Contact_Organization", null=True, blank=True)
    contact = models.ForeignKey("ContactOrOrganization", related_name="contact", null=True, blank=True)
    connection_type = models.CharField(max_length=60, choices=CHOICES)

Thanks in advance for any help,
Bruce

Mike Dewhirst

unread,
Apr 27, 2016, 2:15:56 AM4/27/16
to django...@googlegroups.com
On 26/04/2016 9:41 PM, Bruce Whealton wrote:
> Mike,
> Â Â Â Â Â So, I tried your idea for reorganizing the models, and
> just removed Organization and instead setup
> ContactOrOrganization as a class. Â
> It seemed to work ok, in terms of migrating fine. Â However, the
> database now lacks a ContactsOrOrganizationÂ
> table. Â
> Oops, my mistake, it does have a table now for that model. Â I wonder if
> I need a ForeignKey field in the ContactsOrOrganization table?

Bruce

I thought I'd better try it myself and this models.py in an app called
"contact" works for me. I'm using Python 3.4 and Django 1.8.12 on
Windows 8.1 with Postgres 9.3 on localhost.

Because I kept doing typos I changed that ridiculously long name to
Entity. Also, mindful of your requirement to have different types of
entities namely people and organisations, I did a a Role model to carry
that information. In fact I could have done a choices= attribute on
Entity but I might use this myself and I would prefer the 1:n approach
because an entity could be more than just one sort of entity.

Anyway, once the initial migration for the following models.py is run
and the tables exist it is easy to flesh out any of the models with more
fields and/or other related tables and rely on migrate to roll out the
changes.

from django.db import models

class Role(models.Model):

name = models.CharField(max_length=32)

class Meta:
verbose_name = 'Entity type'
verbose_name_plural = 'Entity types'

def __str__(self):
return "{0}".format(self.name)


class Entity(models.Model):

connections = models.ManyToManyField("self",
blank=True,
symmetrical=False,
through="Entity_Connections")

role = models.ForeignKey("role")

name = models.CharField(max_length=128)

comment = models.TextField(null=True, blank=True)

class Meta:
verbose_name = 'Entity'
verbose_name_plural = 'Entities'

def __str__(self):
return "{0} {{1})".format(self.name, self.role)

class Entity_Connections(models.Model):

from_entity = models.ForeignKey("entity",
related_name="from_entity")

to_entity = models.ForeignKey("entity", blank=True,
related_name="to_entity")

class Meta:
verbose_name = 'Connection'
verbose_name_plural = 'Connnections'


Good luck

Mike


> Bruce
>
>
> Anyway, I will try to remove the ManyToMany statement from the
> Connections Model. Â
> I would then have one Contact or Organization maps to many Connection
> types. Â
>
> On Sunday, April 24, 2016 at 1:13:16 AM UTC-4, Mike Dewhirst wrote:
>
>
> I think you should rethink your Contact and Organization classes and
> see
> if you can eliminate one or the other. A single table for both would
> simplify the problem because the Connection class can implement as many
> connections as you like.
>
> For example ...
>
> class ContactOrOrganization(etc):
> Â Â Â various detail fields ...
>
> class Connection(etc):
> Â Â Â organization = ForeignKey("ContactOrOrganization",
> Â Â Â Â Â related_name="organization")
> Â Â Â contact = ForeignKey("ContactOrOrganization",
> Â Â Â Â Â related_name="contact")
>
> Just because I used related_name that way means nothing. You can
> connect
> contacts together or organizations together. Also, you can add other
> fields to Connection with which to describe the relationship.
>
> Mike
>
> > I wanted to use the Organization as a foreign key on the Contact
> model.
> > Â I could have more than one contact from
> > an Organization. Â The Connection model is inspired by the Google
> Plus
> > idea of "Circles" - e.g. friends,
> > family, following, etc. Â  So, this would be a many-to-many
> relationship. Â
> >
> > My problems are (1) I cannot create connections without specifying a
> > contact. Â
> > (2) If I was adding a contact using the admin interface, how do I
> allow
> > no value for that foreign field
> > or allow for some kind of ajax type of text completion? Â If a
> person is
> > family or friend, I may not need
> > to list an Organization for them.
> > (3) I would like to support multiple connection types - e.g.
> following,
> > employer, etc.
> >
> > So, here is my apps models.py file:
> > Â >>>>
> >
> > from django.db import models
> >
> >
>
> --
> You received this message because you are subscribed to the Google
> Groups "Django users" group.
> To unsubscribe from this group and stop receiving emails from it, send
> an email to django-users...@googlegroups.com
> <mailto:django-users...@googlegroups.com>.
> To post to this group, send email to django...@googlegroups.com
> <mailto:django...@googlegroups.com>.
> Visit this group at https://groups.google.com/group/django-users.
> To view this discussion on the web visit
> https://groups.google.com/d/msgid/django-users/32dfa22a-0a55-4fdf-9da0-73152b1223aa%40googlegroups.com
> <https://groups.google.com/d/msgid/django-users/32dfa22a-0a55-4fdf-9da0-73152b1223aa%40googlegroups.com?utm_medium=email&utm_source=footer>.

Bruce Whealton

unread,
May 2, 2016, 11:37:44 AM5/2/16
to Django users
As an update to this...  I decided to simplify things a bit.  For my contacts application, 
I decided to start with just one table.  I got that working in a Ubuntu Vbox 
environment, inside a virtualenv. 

I'm not sure if it is ok to use python manage.py makemigrations on the 
production server or if one should just do that locally and then import it.
Thanks for any advice,
This should be part of a larger posting on workflows moving from development to
production.
Thanks,
Bruce

Michal Petrucha

unread,
May 2, 2016, 11:48:52 AM5/2/16
to django...@googlegroups.com
On Mon, May 02, 2016 at 08:37:43AM -0700, Bruce Whealton wrote:
> I'm not sure if it is ok to use python manage.py makemigrations on the
> production server or if one should just do that locally and then import it.

Hi Bruce,

Migrations are a part of your application's code base, and they should
be treated as such. In other words, you create all migrations during
development, and make sure to include them in your code repository,
and then as part of your deployment strategy, you only apply them in
the production environment.

Cheers,

Michal
signature.asc

Bruce Whealton

unread,
May 2, 2016, 5:31:37 PM5/2/16
to Django users
Michal,
       I had to read your response a few times but I finally got it.  I was reading
that all migrations are created in development only and then applied during 
production.  
So, during development you use makemigrations and then when deployed
you migrate.  

Thus, if you add a new feature, you make the migration on the 
development system then push it to producation, where you can
issue the command python manage.py migrate.  

I just thought of something that is related to migrations.  Suppose,
with my contacts app, I want to change the name field to be
first_name + last_name.  If there is data in the database, how will
this be handled?

I'll give it a try and see what happens.  It is just the development machine
that has the data.
Thanks,
Bruce

Michal Petrucha

unread,
May 3, 2016, 3:49:49 AM5/3/16
to django...@googlegroups.com
Hi Bruce,

On Mon, May 02, 2016 at 02:31:37PM -0700, Bruce Whealton wrote:
> Michal,
> I had to read your response a few times but I finally got it. I was
> reading
> that all migrations are created in development only and then applied during
> production.
> So, during development you use makemigrations and then when deployed
> you migrate.
>
> Thus, if you add a new feature, you make the migration on the
> development system then push it to producation, where you can
> issue the command python manage.py migrate.

Yes, that sounds correct.

> I just thought of something that is related to migrations. Suppose,
> with my contacts app, I want to change the name field to be
> first_name + last_name. If there is data in the database, how will
> this be handled?


There's a description of almost exactly this kind of migration in the
docs:
https://docs.djangoproject.com/en/1.9/topics/migrations/#data-migrations

To give you a more complete overview of how to do that, first, you
create a migration to add the new columns, after which you should have
both the old columns, and the new ones in the database at the same
time. This is a regular schema migration.

Then, you create a data migration which will read data from the old
columns, do any processing, and save the data in new columns. This is
usually called a data migration, and is achieved using the RunPython
migration operation. Alternatively, if you are comfortable with SQL,
and if you are able to express the data transformation in a few SQL
queries, you can use RunSQL instead.

Finally, you create a third migration – another schema migration, in
which you just remove the old columns.

If you want to be able to apply the data migration in reverse, you'll
also have to define a function (or SQL statements) that will perform
the reverse operation. This is optional, but it might come in handy
sometimes, such as when switching between development branches where
each branch has a different set of migrations.


As a final note, you can use the same technique for example if you
want to add a non-nullable unique field to a database that already
contains data. The docs contain a quick walkthrough for this kind of
operation:
https://docs.djangoproject.com/en/1.9/howto/writing-migrations/#migrations-that-add-unique-fields


Does this help?

Michal
signature.asc
Reply all
Reply to author
Forward
0 new messages