from django.db import models
from datetime import datetime

# M2M described on one of the models
class PersonExplicitOneWay(models.Model):
    name = models.CharField(max_length=128)

    def __unicode__(self):
        return self.name

class GroupExplicitOneWay(models.Model):
    name = models.CharField(max_length=128)
    members = models.ManyToManyField(PersonExplicitOneWay, through='MembershipExplicitOneWay')
    
    def __unicode__(self):
        return self.name

class MembershipExplicitOneWay(models.Model):
    person = models.ForeignKey(PersonExplicitOneWay)
    group = models.ForeignKey(GroupExplicitOneWay)
    date_joined = models.DateTimeField(default=datetime.now)

>>> bob = PersonExplicitOneWay(name = 'Bob')
>>> bob.save()
>>> jim = PersonExplicitOneWay(name = 'Jim')
>>> jim.save()
>>> jane = PersonExplicitOneWay(name = 'Jane')
>>> jane.save()
>>> rock = GroupExplicitOneWay(name = 'Rock')
>>> rock.save()
>>> roll = GroupExplicitOneWay(name = 'Roll')
>>> roll.save()

>>> rock.members.add(jim, jane)
>>> rock.members.all()
[<PersonExplicitOneWay: Jim>, <PersonExplicitOneWay: Jane>]

>>> roll.members.add(bob, jim)
>>> roll.members.all()
[<PersonExplicitOneWay: Bob>, <PersonExplicitOneWay: Jim>]

>>> jane.groupexplicitoneway_set.all()
[<GroupExplicitOneWay: Rock>]

>>> jane.groupexplicitoneway_set.add(roll)
>>> jane.groupexplicitoneway_set.all()
[<GroupExplicitOneWay: Rock>, <GroupExplicitOneWay: Roll>]

>>> jim.groupexplicitoneway_set.all()
[<GroupExplicitOneWay: Rock>, <GroupExplicitOneWay: Roll>]


# M2M described on two of the models
class PersonExplicitTwoWays(models.Model):
    name = models.CharField(max_length=128)
    groups = models.ManyToManyField(PersonExplicitTwoWays, through='MembershipExplicitTwoWays')
    
    def __unicode__(self):
        return self.name

class GroupExplicitTwoWays(models.Model):
    name = models.CharField(max_length=128)
    members = models.ManyToManyField(PersonExplicitTwoWays, through='MembershipExplicitTwoWays')
    
    def __unicode__(self):
        return self.name

class MembershipExplicitTwoWays(models.Model):
    person = models.ForeignKey(PersonExplicitTwoWays)
    group = models.ForeignKey(GroupExplicitTwoWays)
    date_joined = models.DateTimeField(default=datetime.now)

>>> bob = PersonExplicitTwoWays(name = 'Bob')
>>> bob.save()
>>> jim = PersonExplicitTwoWays(name = 'Jim')
>>> jim.save()
>>> jane = PersonExplicitTwoWays(name = 'Jane')
>>> jane.save()
>>> rock = GroupExplicitTwoWays(name = 'Rock')
>>> rock.save()
>>> roll = GroupExplicitTwoWays(name = 'Roll')
>>> roll.save()

>>> rock.members.add(jim, jane)
>>> rock.members.all()
[<PersonExplicitTwoWays: Jim>, <PersonExplicitTwoWays: Jane>]

>>> roll.members.add(bob, jim)
>>> roll.members.all()
[<PersonExplicitTwoWays: Bob>, <PersonExplicitTwoWays: Jim>]

>>> jane.groups.all()
[<GroupExplicitTwoWays: Rock>]

>>> jane.groups.add(roll)
>>> jane.groups.all()
[<GroupExplicitTwoWays: Rock>, <GroupExplicitTwoWays: Roll>]

>>> jim.groups.all()
[<GroupExplicitTwoWays: Rock>, <GroupExplicitTwoWays: Roll>]


# M2M implied by model with dual foreign keys
class PersonImplicit(models.Model):
    name = models.CharField(max_length=128)
    
    def __unicode__(self):
        return self.name

class GroupImplicit(models.Model):
    name = models.CharField(max_length=128)
    
    def __unicode__(self):
        return self.name

class MembershipImplicit(models.Model):
    person = models.ForeignKey(PersonImplicit)
    group = models.ForeignKey(GroupImplicit)
    date_joined = models.DateTimeField(default=datetime.now)
    #Do we need/want some sort of a flag?
    #Maybe like the following...
    #class Meta:
    #    many_to_many = ('person', 'group')

>>> bob = PersonImplicit(name = 'Bob')
>>> bob.save()
>>> jim = PersonImplicit(name = 'Jim')
>>> jim.save()
>>> jane = PersonImplicit(name = 'Jane')
>>> jane.save()
>>> rock = GroupImplicit(name = 'Rock')
>>> rock.save()
>>> roll = GroupImplicit(name = 'Roll')
>>> roll.save()

>>> rock.personimplicit_set.add(jim, jane)
>>> rock.personimplicit_set.all()
[<PersonImplicit: Jim>, <PersonImplicit: Jane>]

>>> roll.personimplicit_set.add(bob, jim)
>>> roll.personimplicit_set.all()
[<PersonImplicit: Bob>, <PersonImplicit: Jim>]

>>> jane.groupimplicit_set.all()
[<GroupImplicit: Rock>]

>>> jane.groupimplicit_set.add(roll)
>>> jane.groupimplicit_set.all()
[<GroupImplicit: Rock>, <GroupImplicit: Roll>]

>>> jim.groupimplicit_set.all()
[<GroupImplicit: Rock>, <GroupImplicit: Roll>]