📂
SEI 1019
  • Introduction
  • About These Notes
  • Syllabus
  • Development Workflow
    • Command Line
      • The Terminal
      • Filesystem Navigation
      • File Manipulation
      • Additional Topics
    • Intro to Git
      • Version Control
      • Local Git
      • Remote Git
      • Git Recipes
    • Group Collaboration
      • Git Workflows
      • Project Roles and Tools
    • VS Code Tips & Tricks
  • HTML/CSS
    • HTML
    • CSS Selectors
    • CSS Box Model and Positioning
      • Box Model
      • Display and Positioning
      • Flexbox
      • Grid
      • Flexbox & Grid Games
      • Floats and Clears
      • Additional Topics
    • Advanced CSS
      • Responsive Design
      • Pseudo-Classes/Elements
      • Vendor Prefixes
      • Custom Properties
      • Additional Topics
    • Bootstrap
    • CSS Frameworks
    • Accessibility
  • JavaScript
    • Primitives
    • Arrays
    • Objects
    • Control Flow
      • Boolean Expressions
      • Conditionals
      • Loops
      • Promises
    • Functions
      • Callbacks
      • Timing Functions
      • Iterators
    • DOM and Events
    • DOM Manipulation
    • HTML5 Canvas
    • How To Reduce Redundancy
    • (2019) JavaScript OOP
    • (2016) OOP with Classes
    • (1995) OOP with Prototypes
      • Constructors
      • Prototypes
    • Intro to TDD
    • Scoping
    • Inheritance
      • Prototypal Inheritance
      • Call, Apply, and other Functions
      • ES6 Inheritance
      • Resources
    • Custom Node Modules
    • Additional Topics
      • AJAX, Fetch, and Async/Await
      • AJAX w/JSON and Localstorage
        • AJAX w/JSON
        • Local Storage
      • Async module
      • Data Scraping
  • jQuery
    • Intro
      • DOM Manipulation
      • Reddit Practice
      • Styling
      • Events
    • Plugins
    • AJAX
  • APIs
    • Fetch
    • AJAX w/jQuery
    • AJAX w/Fetch
  • Databases
    • Intro to SQL
    • Advanced SQL
    • MongoDB
      • Intro to NoSQL
      • CRUD in MongoDB
      • Data Modeling
      • Intermediate Mongo
  • Node/Express
    • Node
      • Intro to Node
      • Node Modules
      • Node Package Manager (NPM)
    • Express
      • Intro to Express
        • Routes
        • Views
        • Templates
        • Layouts and Controllers
        • CRUD & REST
          • Get and Post
          • Put and Delete
      • APIs with Express (request)
      • APIs with Express (axios)
    • Sequelize
      • Terminology
      • Setup
      • Using Models
      • Seeding Data
      • Validations and Migrations
      • Resources
      • 1:M Relationships
      • N:M Relationships
    • Express Authentication
      • Research Components
      • Code Components
      • Auth in Theory
        • Sessions
        • Passwords
        • Middleware
        • Hooks
      • Auth in Practice
        • Create the User
        • User Signup
        • Sessions
        • User Login
        • Authorization and Flash messages
    • Testing with Mocha and Chai
    • Mongoose
      • Mongoose Associations
    • JSON Web Tokens
      • Codealong
    • Additional Topics
      • oAuth
      • Geocoding with Mapbox
      • Geocoding and Google Maps
      • Cloudinary
      • Websockets with Socket.io
      • SASS
  • Ruby
    • Intro to Ruby
    • Ruby Exercises
    • Ruby Classes
    • Ruby Testing with Rspec
    • Ruby Inheritance
    • Ruby Data Scraping
  • Ruby on Rails
    • Intro to Rails
    • APIs with Rails
    • Asset Pipeline
    • Rails Auth and 1-M
      • Auth Components
    • Rails N:M
    • ActiveRecord Polymorphism
    • Additional Topics
      • oAuth
      • SASS
      • Rails Mailers
      • Cloudinary
      • Jekyll
  • React (Updated 2019)
    • ES6+/ESNext
      • Const and Let
      • Arrow Functions
      • Object Literals and String Interpolation
      • ES6 Recap
      • ES6 Activity
    • Intro to React
      • Create React App
      • Components and JSX
      • Virtual DOM
      • Props
      • Dino Blog Activity
      • Nested Components
      • Lab: LotR
    • React State
      • Code-Along: Mood Points
      • Code-Along: Edit Dino Blog
      • Lab: Simple Calc
      • Lifting State
    • React Router
      • Browser History/SPAs
      • React Router (lesson and full codealong)
      • Router Lab
    • Fetch and APIs
      • APIs with Fetch and Axios
      • Fetch the Weather
    • React Hooks
    • React LifeCycle
      • Lab: Component LifeCycle
    • React Deployment
    • Additional Topics
      • React Frameworks
        • Material UI Theming
      • Typescript
        • More Types and Syntax
        • Tsconfig and Declaration Files
        • Generics with Linked List
      • Redux
      • TypeScript
      • Context API
      • React Native
  • Meteor
  • Deployment and Config
    • Installfest
      • Mac OSX
      • Linux
      • Git Configuration
      • Sublime Packages
    • Deploy - Github Pages
    • Deploy - Node/Sequelize
    • Deploy - Node/MongoDB
    • Deploy React
    • Deploy - Rails
      • Foreman (Environment Variables)
    • Deploy - AWS Elastic Beanstalk
    • Deploy - S3 Static Sites
    • Deploy - Django
    • Deploy - Flask
  • Data Structures and Algorithms
    • Recursion
    • Problem Solving - Array Flatten
    • Binary Search
    • Algorithm Complexity
    • Stacks and Queues
    • Bracket Matching
    • Ruby Linked Lists
      • Sample Code
      • Beginner Exercises
      • Advanced Exercises
    • JS Linked Lists
      • Sample Code
      • Beginner Exercises
      • Beginner Solutions
    • Hash Tables
    • Intro to Sorting
    • Insertion Sort
    • Bucket Sort
    • Bubble Sort
    • Merge Sort
    • Quick Sort
    • Heap Sort
    • Sorting Wrapup
    • Hashmaps
    • Trees and Other Topics
  • Python
    • Python Installation
    • Intro to Python
    • Python Lists
    • Python Loops
    • Python Dictionaries
    • Python Sets and Tuples
    • Python Cheatsheet
    • Python Functions
    • Python Classes
    • Python Class Inheritance
    • Intro to Flask
    • Intro to SQLAlchemy
      • Flask and SQLAlchemy
    • Using PyMongo
    • Intro to Django
    • CatCollector CodeAlong
      • URLs, Views, Templates
      • Models, Migrations
      • Model Form CRUD
      • One-to-Many Relations
      • Many-to-Many Relations
      • Django Auth
    • Django Cheatsheet
    • Django Auth
    • Django Polls App Tutorial
    • Django School Tool Tutorial
    • Django 1:M Relationships
    • Custom Admin Views
    • Data Structures and Algorithms
      • Recursion
      • Binary Search
      • Stacks and Queues
      • Linked Lists
      • Binary Trees
      • Bubble Sort
      • TensorFlow & Neural Networks
    • Adjacent Topics
      • Raspberry Pi
      • Scripting
  • Assorted Topics
    • History of Computer Science
    • Regular Expressions
    • Intro to WDI (Course Info)
    • Being Successful in WDI
    • Internet Fundamentals
      • Internet Lab
    • User Stories and Wireframing
      • Wireframing Exercise: Build an Idea
    • Post WDI
      • Learning Resources
      • Deliverables -> Portfolio
      • FAQ
  • Projects
    • Project 1
    • Project 2
    • Project 3
      • Project 3 Pitch Guidelines
    • Project 4
    • Past Projects
      • Project 1
      • Project 2
      • Project 3
      • Project 4
      • Portfolios
    • Post Project 2
    • MEAN Hackathon
      • Part 1: APIs
      • Part 2: Angular
    • Portfolio
  • Web Development Trends
  • Resources
    • APIs and Data
    • Tech Websites
    • PostgreSQL Cheat Sheet
    • Sequelize Cheat Sheet
    • Database Administration
  • Archived Section
    • (Archived) ReactJS
      • Intro to React
        • Todo List Codealong
        • Additional Topics
      • Deploy React
      • React with Gulp and Browserify
        • Setting up Gulp
        • Additional Gulp Tasks
      • React Router
        • OMDB Router
        • OMDB Search
        • Additional Resources
      • React Animations
        • CSS Animations
    • AngularJS
      • Intro to AngularJS
        • Components and SPA
        • Create an Angular App
      • Angular Directives and Filters
      • Angular Animation
      • Angular Bootstrap Directives
        • Bootstrap Modals
      • Angular $http
      • Angular Services
        • Service Recipes
        • ngResource
        • Star Wars Codealong
      • Angular Routing
      • Angular + Express
      • Angular Authentication
        • Additional Topics
      • Angular Components
      • Angular Custom Filters
      • Angular Custom Directives
Powered by GitBook
On this page
  • Learning Objectives
  • Adding the CatToy Model
  • Register the new model
  • CRUD Routes for the New CatToy Model
  • URLs
  • Views
  • Templates
  • TEST!
  • Adding the Many-to-Many Field to the Model
  • Add cattoys to the Cat show page
  • Add Cat owners to CatToy detail page
  • Conclusion

Was this helpful?

  1. Python
  2. CatCollector CodeAlong

Many-to-Many Relations

Learning Objectives

  1. Use Django's ManyToManyField to enable a m:m relationship

  2. Use mainmodel.submodels and submodel.mainmodel_set to access related data from either Model.

Yesterday we learned the first of two common types of data relations: One-to-Many or 1:m. In this relationship, one model is said to "own" multiple objects of another model. It's a very common relationship and we can readily think of examples: one customer has many orders; one conference has many attendees; one restaurant has many menu items; one collector has many cats.

The next kind of relationship we will learn about is the Many-to-Many relationship, often abbreviated m:m or n:m. You can think of this as a two-way one-to-many relationship where each model "owns" multiple objects of the other model. Some examples of this include: one band can have many genres of music that they play, and one genre has many bands in it; one article has many category tags, and each category tag is linked to many articles; and today's example - one cat can have many toys, and each toy could be owned by multiple cats.

If we were writing all of this ourselves we would normally need an intermediary table, called a join table, to enable the references between tables. We use a join table because if we didn't, the nature of a many-to-many relationship would require one of our related tables to contain duplicate data. BAD!

We aren't writing it ourselves, though. (Not yet, anyway.) Django has a very easy way to link models together in a many-to-many relationship that takes care of the join table and references in the database for us. Let's see how it works.

Adding the CatToy Model

We are creating a new model to represent toys that our cat owns. We will call it CatToy. Each CatToy can be owned by many Cats and each Cat can own many CatToys. Open up your main_app/models.py and add a class for the CatToy model above the Cat model class:

class CatToy(models.Model):
    name = models.CharField(max_length=100)
    color = models.CharField(max_length=50)

    def __str__(self):
        return self.name

We don't need to represent much data about the toys to set up the relationship. We've included a __str__ method so that the Model will nicely print its name. We also included a get_absolute_url() method that will allow us to omit all the success_urls and redirect URLs from our generic editing views. Once we've added this, save the file, make new migrations, and run them:

python3 manage.py makemigrations
python3 manage.py migrate

Register the new model

Go ahead and add this model to the main_app/admin.py:

# Add the CatToy model to this import line
from .models import Cat, CatToy

# Register the model below the others
...
admin.site.register(CatToy)

Now we can easily read, create, update, and delete CatToys via the admin interface. Let's now add some basic CRUD routes for it.

CRUD Routes for the New CatToy Model

We do need the ability to create, read, update, and delete each CatToy since it is one of our models. Let's quickly set up URLs, views, and template forms for that just like we did in the CRUD Forms lesson:

URLs

We will need 5 total routes for this new Model: read all, read one, create one, update one, delete one. The URLs will largely follow the same pattern as the ones for Cat. Here are all 5 that we need to add. In the interest of time, just paste these into the urlpatterns list in your main_app/urls.py:

path('cattoys/', views.cattoys_index, name='cattoys_index'),
path('cattoys/<int:cattoy_id>', views.cattoys_show, name='cattoys_show'),
path('cattoys/create/', views.CatToyCreate.as_view(), name='cattoys_create'),
path('cattoys/<int:pk>/update/', views.CatToyUpdate.as_view(), name='cattoys_update'),
path('cattoys/<int:pk>/delete/', views.CatToyDelete.as_view(), name='cattoys_delete'),

Views

We can also use the same patterns for our corrsponding view functions as we used for Cat. Don't forget to import your CatToy model, then add these into your main_app/views.py:

# import the CatToy model
from .models import Cat, CatToy

def cattoys_index(request):
    cattoys = CatToy.objects.all()
    return render(request, 'cattoys/index.html', {'cattoys': cattoys})

def cattoys_show(request, cattoy_id):
    cattoy = CatToy.objects.get(id=cattoy_id)
    return render(request, 'cattoys/show.html', {'cattoy': cattoy})

class CatToyUpdate(UpdateView):
    model = CatToy
    fields = ['name', 'color']
    success_url = '/cattoys'

class CatToyDelete(DeleteView):
    model = CatToy
    success_url = '/cattoys'

Templates

Lastly, we need a few templates for this new model. We need the two forms for our generic editing views and we need an index and a details page. Our two forms will go into main_app/templates/main_app. One will be cattoy_form.html and the other will be cattoy_confirm_delete.html. Recall that this is the naming convention for these form templates when we are using generic editing views. Let's add those now:

<!-- main_app/templates/main_app/cattoy_form.html -->
{% extends 'base.html' %}

{% block content %}
    <form action="" method="post">
        {% csrf_token %}
        <table>
            {{ form.as_table }}
        </table>
        <input type="submit" value="Submit!">
    </form>
{% endblock %}
<!-- main_app/templates/main_app/cattoy_confirm_delete.html -->
{% extends 'base.html' %}

{% block content %}
    <h1>Delete Cat Toy</h1>

    <h4>Are you sure you want to delete <strong>{{ cattoy }}</strong>?</h4>

    <form action="" method="POST">
        {% csrf_token %}
        <input type="submit" value="Yes - Delete!">
        <a href="{% url 'cattoys_show' cattoy.id %}">Cancel</a>
    </form>
{% endblock %}

Now we'll add the two "read" pages, cattoys\index.html and cattoys\show.html. We need to make a directory inside our templates directory named cattoys and our pages will go in there:

<!-- templates/cattoys/show.html -->
{% extends 'base.html' %}
{% block content %}

<h1>Cat Toy Details</h1>

<div>
    <div>
        <span>{{ cattoy.name }}</span>
        <p>Color: {{ cattoy.color }}</p>
    </div>
    <div>
        <a href="{% url 'cattoys_update' cattoy.id %}">Edit</a>
        <a href="{% url 'cattoys_delete' cattoy.id %}">Delete</a>
    </div>
</div>
{% endblock %}
<!-- templates/cattoys/index.html -->
{% extends 'base.html' %}
{% block content %}

<h1>Cat Toy List</h1>

{% for cattoy in cattoys %}
    <div>
        <a href="{% url 'cattoys_show' cattoy.id %}">
            <div class="card-content">
                <span>{{ cattoy.name }}</span>
                <p>Color: {{ cattoy.color }}</p>
            </div>
        </a>
    </div>
{% endfor %}

{% endblock %}

TEST!

This is a great place to test what we've written. We've just added 5 new routes. We need to test them. Restart your server if it was running and let's test these routes:

  1. Test http://localhost:8000/cattoys/create for creating new cat toys. Add a couple.

  2. Test http://localhost:8000/cattoys for showing the toys you've added.

  3. Click into one of the toys to test the show page.

  4. Test the update link on one of the toys.

  5. Test the delete link for one of the toys.

Adding the Many-to-Many Field to the Model

With that code in place, we can start working on updating our existing Cat Model and pages. The way that we establish a m:m relationship is by adding a ManyToManyField to a model. Django requires only one of our models to have this field and it takes care of the rest. We must only decide which model to put the field into. I think we will mostly be viewing toys by looking at the Cat that owns them so let's add cattoys to the Cat model:

class Cat(models.Model):
    ...
    # Add this line below the other fields
    cattoys = models.ManyToManyField(CatToy)
    ...

You'll notice that this line doesn't know what CatToy is if your CatToy class is below you Cat class. If you're running into this issues, swap the position of the two models in the file.

That is really all we need to do to set up the relationship. But we have changed a model so we now need to generate some new migrations and run them. Open up a terminal and run these commands from your project folder:

python3 manage.py makemigrations

...then...

python3 manage.py migrate

Now we need to update our CatCreate and CatUpdate view classes to include the new field:

# main_app/views.py
class CatCreate(CreateView):
    model = Cat
    # Update the line below - add 'cattoys'
    fields = ['name', 'breed', 'description', 'age', 'cattoys']

class CatUpdate(UpdateView):
    model = Cat
    # Update the line below - add 'cattoys'
    fields = ['name', 'breed', 'description', 'age', 'cattoys']

Save everything, restart the server if necessary and visit the Cat Create page. Yay! Now we can add multiple cat toys to any of our Cats.

Add cattoys to the Cat show page

In the style of our detail pages so far, let's add something to show toys. Right above the for block for the photos in cats/show.html, add this code:

<!-- templates/cats/show.html -->
<!-- above the update and delete links -->
...
{% for cattoy in cat.cattoys.all %}
    <div>{{cattoy.name}}</div>
{% empty %}
    <div>Cat Has No Toys :(</div>
{% endfor %}
...

Add Cat owners to CatToy detail page

Because this is a many-to-many, it probably makes sense to show all the related Cats that own any particular toy. Let's update the cattoys/show.html page.

<!-- templates/cattoys/show.html -->
...
    <div>
        <span>{{ cattoy.name }}</span>
        <p>Color: {{ cattoy.color }}</p>
        <p>Owned by:</p>
        {% for cat in cattoy.cat_set.all %}
            <p>{{cat.name}}</p>
        {% empty %}
            <p>Nobody Has This Toy</p>
        {% endfor %}
    </div>
...

Conclusion

Congratulations! You've completed a substantial full-stack app in Python and Django.

PreviousOne-to-Many RelationsNextDjango Auth

Last updated 4 years ago

Was this helpful?