├── .gitignore ├── README.md ├── apps ├── apps │ ├── __init__.py │ ├── settings.py │ ├── urls.py │ └── wsgi.py ├── books_pc_formset │ ├── __init__.py │ ├── admin.py │ ├── apps.py │ ├── forms.py │ ├── migrations │ │ ├── 0001_initial.py │ │ └── __init__.py │ ├── models.py │ ├── templates │ │ └── books_pc_formset │ │ │ ├── book_confirm_delete.html │ │ │ ├── book_form.html │ │ │ ├── home.html │ │ │ └── nav.html │ ├── tests.py │ ├── urls.py │ └── views.py ├── books_pc_formset2 │ ├── __init__.py │ ├── admin.py │ ├── apps.py │ ├── forms.py │ ├── migrations │ │ ├── 0001_initial.py │ │ └── __init__.py │ ├── models.py │ ├── templates │ │ └── books_pc_formset2 │ │ │ ├── book_confirm_delete.html │ │ │ ├── book_form.html │ │ │ ├── home.html │ │ │ ├── nav.html │ │ │ ├── person_confirm_delete.html │ │ │ └── person_form.html │ ├── tests.py │ ├── urls.py │ └── views.py ├── books_pc_multi_view │ ├── __init__.py │ ├── admin.py │ ├── apps.py │ ├── forms.py │ ├── migrations │ │ ├── 0001_initial.py │ │ └── __init__.py │ ├── models.py │ ├── templates │ │ └── books_pc_multi_view │ │ │ ├── book_confirm_delete.html │ │ │ ├── book_form.html │ │ │ ├── book_view.html │ │ │ ├── home.html │ │ │ ├── nav.html │ │ │ ├── review_confirm_delete.html │ │ │ └── review_form.html │ ├── tests.py │ ├── urls.py │ └── views.py ├── books_pc_multi_view2 │ ├── __init__.py │ ├── admin.py │ ├── apps.py │ ├── forms.py │ ├── migrations │ │ ├── 0001_initial.py │ │ └── __init__.py │ ├── models.py │ ├── templates │ │ └── books_pc_multi_view2 │ │ │ ├── book_confirm_delete.html │ │ │ ├── book_form.html │ │ │ ├── book_view.html │ │ │ ├── home.html │ │ │ ├── nav.html │ │ │ ├── person_confirm_delete.html │ │ │ ├── person_form.html │ │ │ ├── review_confirm_delete.html │ │ │ └── review_form.html │ ├── tests.py │ ├── urls.py │ └── views.py ├── books_simple │ ├── __init__.py │ ├── admin.py │ ├── apps.py │ ├── forms.py │ ├── migrations │ │ ├── 0001_initial.py │ │ └── __init__.py │ ├── models.py │ ├── templates │ │ └── books_simple │ │ │ ├── book_confirm_delete.html │ │ │ ├── book_form.html │ │ │ ├── home.html │ │ │ └── nav.html │ ├── tests.py │ ├── urls.py │ └── views.py ├── manage.py └── theme │ ├── __init__.py │ ├── admin.py │ ├── apps.py │ ├── migrations │ └── __init__.py │ ├── models.py │ ├── static │ └── css │ │ └── style.css │ ├── templates │ ├── base.html │ └── home.html │ ├── tests.py │ └── views.py ├── requirements.txt └── www ├── media └── .gitignore └── static └── .gitignore /.gitignore: -------------------------------------------------------------------------------- 1 | db.sqlite3 2 | *.pyc 3 | __pycache__ 4 | *.swp 5 | venv 6 | .vscode/ -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # Django CRUD Parent/Child Example 3 | 4 | This project consist of five stand alone applications that shows different 5 | ways to implement CRUD operations in single table, and parent/child tables 6 | using Django web framework. 7 | 8 | This version uses Django 2.2 LTS version. 9 | 10 | ## Sample Applications 11 | 12 | The applications are: 13 | 14 | - books_simple: Single table CRUD operations. 15 | - books\_pc\_formset: Parent/Child CRUD operation using Django formsets, which means editing the children in the sample form as the parent. 16 | - books\_pc\_formset2: similar to previous app but uses a foreign key in the children. 17 | - books\_pc\_multiview: Parent/Child CRUD operation using multiple one view for the parent and another seperate view for the children. 18 | - books\_pc\_multiview2: similar to previous app but uses a foreign key in the children. 19 | 20 | ## Installation and Running 21 | 22 | Summary: 23 | 24 | git clone git@github.com:rayed/django-crud-parent-child.git 25 | cd django-crud-parent-child 26 | python3 -m venv venv 27 | . venv/bin/activate 28 | pip install -r requirements.txt 29 | cd apps 30 | ./manage.py migrate 31 | ./manage.py runserver 32 | 33 | ./manage.py createsuperuser 34 | 35 | Description of the installation steps: 36 | 37 | # Clone the project 38 | git clone git@github.com:rayed/django-crud-parent-child.git 39 | 40 | cd django-crud-parent-child 41 | 42 | # Create Python 3 virtual environment 43 | python3 -m venv venv 44 | # Activate the virtual environment 45 | . venv/bin/activate 46 | 47 | # Install required packages (Django 2.2 LTS) 48 | pip install -r requirements.txt 49 | 50 | cd apps 51 | # Create database tables for the project, project configured to use SQLite DB 52 | ./manage.py migrate 53 | 54 | # Run the development server 55 | ./manage.py runserver 56 | 57 | -------------------------------------------------------------------------------- /apps/apps/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rayed/django-crud-parent-child/982f70f1ad762fca52500de09555371f1bcba8ac/apps/apps/__init__.py -------------------------------------------------------------------------------- /apps/apps/settings.py: -------------------------------------------------------------------------------- 1 | """ 2 | Django settings for apps project. 3 | 4 | Generated by 'django-admin startproject' using Django 2.2.13. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/2.2/topics/settings/ 8 | 9 | For the full list of settings and their values, see 10 | https://docs.djangoproject.com/en/2.2/ref/settings/ 11 | """ 12 | 13 | import os 14 | 15 | # Build paths inside the project like this: os.path.join(BASE_DIR, ...) 16 | BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) 17 | 18 | 19 | # Quick-start development settings - unsuitable for production 20 | # See https://docs.djangoproject.com/en/2.2/howto/deployment/checklist/ 21 | 22 | # SECURITY WARNING: keep the secret key used in production secret! 23 | SECRET_KEY = '408jjoy!o)hen3g&ew6g2#du$ql#6m8l(_m0vkr8n=m#u$7k)y' 24 | 25 | # SECURITY WARNING: don't run with debug turned on in production! 26 | DEBUG = True 27 | 28 | ALLOWED_HOSTS = [] 29 | 30 | 31 | # Application definition 32 | 33 | INSTALLED_APPS = [ 34 | 'theme.apps.ThemeConfig', 35 | 'books_simple.apps.BooksSimpleConfig', 36 | 'books_pc_formset.apps.BooksPcFormsetConfig', 37 | 'books_pc_formset2.apps.BooksPcFormset2Config', 38 | 'books_pc_multi_view.apps.BooksPcMultiViewConfig', 39 | 'books_pc_multi_view2.apps.BooksPcMultiView2Config', 40 | 'django.contrib.admin', 41 | 'django.contrib.auth', 42 | 'django.contrib.contenttypes', 43 | 'django.contrib.sessions', 44 | 'django.contrib.messages', 45 | 'django.contrib.staticfiles', 46 | ] 47 | 48 | MIDDLEWARE = [ 49 | 'django.middleware.security.SecurityMiddleware', 50 | 'django.contrib.sessions.middleware.SessionMiddleware', 51 | 'django.middleware.common.CommonMiddleware', 52 | 'django.middleware.csrf.CsrfViewMiddleware', 53 | 'django.contrib.auth.middleware.AuthenticationMiddleware', 54 | 'django.contrib.messages.middleware.MessageMiddleware', 55 | 'django.middleware.clickjacking.XFrameOptionsMiddleware', 56 | ] 57 | 58 | ROOT_URLCONF = 'apps.urls' 59 | 60 | TEMPLATES = [ 61 | { 62 | 'BACKEND': 'django.template.backends.django.DjangoTemplates', 63 | 'DIRS': [], 64 | 'APP_DIRS': True, 65 | 'OPTIONS': { 66 | 'context_processors': [ 67 | 'django.template.context_processors.debug', 68 | 'django.template.context_processors.request', 69 | 'django.contrib.auth.context_processors.auth', 70 | 'django.contrib.messages.context_processors.messages', 71 | ], 72 | }, 73 | }, 74 | ] 75 | 76 | WSGI_APPLICATION = 'apps.wsgi.application' 77 | 78 | 79 | # Database 80 | # https://docs.djangoproject.com/en/2.2/ref/settings/#databases 81 | 82 | DATABASES = { 83 | 'default': { 84 | 'ENGINE': 'django.db.backends.sqlite3', 85 | 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), 86 | } 87 | } 88 | 89 | 90 | # Password validation 91 | # https://docs.djangoproject.com/en/2.2/ref/settings/#auth-password-validators 92 | 93 | AUTH_PASSWORD_VALIDATORS = [ 94 | { 95 | 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', 96 | }, 97 | { 98 | 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', 99 | }, 100 | { 101 | 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', 102 | }, 103 | { 104 | 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', 105 | }, 106 | ] 107 | 108 | 109 | # Internationalization 110 | # https://docs.djangoproject.com/en/2.2/topics/i18n/ 111 | 112 | LANGUAGE_CODE = 'en-us' 113 | 114 | TIME_ZONE = 'UTC' 115 | 116 | USE_I18N = True 117 | 118 | USE_L10N = True 119 | 120 | USE_TZ = True 121 | 122 | 123 | # Static files (CSS, JavaScript, Images) 124 | # https://docs.djangoproject.com/en/2.2/howto/static-files/ 125 | 126 | STATIC_URL = '/static/' 127 | 128 | DEFAULT_AUTO_FIELD = 'django.db.models.AutoField' -------------------------------------------------------------------------------- /apps/apps/urls.py: -------------------------------------------------------------------------------- 1 | """apps URL Configuration 2 | 3 | The `urlpatterns` list routes URLs to views. For more information please see: 4 | https://docs.djangoproject.com/en/2.2/topics/http/urls/ 5 | Examples: 6 | Function views 7 | 1. Add an import: from my_app import views 8 | 2. Add a URL to urlpatterns: path('', views.home, name='home') 9 | Class-based views 10 | 1. Add an import: from other_app.views import Home 11 | 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home') 12 | Including another URLconf 13 | 1. Import the include() function: from django.urls import include, path 14 | 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) 15 | """ 16 | from django.contrib import admin 17 | from django.urls import path, include 18 | 19 | import theme.views 20 | 21 | urlpatterns = [ 22 | path('admin/', admin.site.urls), 23 | 24 | path('', theme.views.home, name='home'), 25 | path('books_simple/', include('books_simple.urls')), 26 | path('books_pc_formset/', include('books_pc_formset.urls')), 27 | path('books_pc_formset2/', include('books_pc_formset2.urls')), 28 | path('books_pc_multi_view/', include('books_pc_multi_view.urls')), 29 | path('books_pc_multi_view2/', include('books_pc_multi_view2.urls')), 30 | ] 31 | -------------------------------------------------------------------------------- /apps/apps/wsgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | WSGI config for apps project. 3 | 4 | It exposes the WSGI callable as a module-level variable named ``application``. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/2.2/howto/deployment/wsgi/ 8 | """ 9 | 10 | import os 11 | 12 | from django.core.wsgi import get_wsgi_application 13 | 14 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'apps.settings') 15 | 16 | application = get_wsgi_application() 17 | -------------------------------------------------------------------------------- /apps/books_pc_formset/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rayed/django-crud-parent-child/982f70f1ad762fca52500de09555371f1bcba8ac/apps/books_pc_formset/__init__.py -------------------------------------------------------------------------------- /apps/books_pc_formset/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | from books_pc_formset.models import Book, Tag 3 | 4 | 5 | class TagInline(admin.TabularInline): 6 | model = Tag 7 | 8 | class BookAdmin(admin.ModelAdmin): 9 | inlines = [ 10 | TagInline, 11 | ] 12 | 13 | admin.site.register(Book, BookAdmin) -------------------------------------------------------------------------------- /apps/books_pc_formset/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class BooksPcFormsetConfig(AppConfig): 5 | name = 'books_pc_formset' 6 | -------------------------------------------------------------------------------- /apps/books_pc_formset/forms.py: -------------------------------------------------------------------------------- 1 | from django.forms import ModelForm 2 | 3 | from .models import Book 4 | 5 | class BookForm(ModelForm): 6 | class Meta: 7 | model = Book 8 | fields = ['name', 'pages'] -------------------------------------------------------------------------------- /apps/books_pc_formset/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.13 on 2020-06-25 16:03 2 | 3 | from django.db import migrations, models 4 | import django.db.models.deletion 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | initial = True 10 | 11 | dependencies = [ 12 | ] 13 | 14 | operations = [ 15 | migrations.CreateModel( 16 | name='Book', 17 | fields=[ 18 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 19 | ('name', models.CharField(max_length=200)), 20 | ('pages', models.IntegerField()), 21 | ], 22 | ), 23 | migrations.CreateModel( 24 | name='Tag', 25 | fields=[ 26 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 27 | ('name', models.CharField(max_length=200)), 28 | ('weight', models.IntegerField()), 29 | ('book', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='books_pc_formset.Book')), 30 | ], 31 | ), 32 | ] 33 | -------------------------------------------------------------------------------- /apps/books_pc_formset/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rayed/django-crud-parent-child/982f70f1ad762fca52500de09555371f1bcba8ac/apps/books_pc_formset/migrations/__init__.py -------------------------------------------------------------------------------- /apps/books_pc_formset/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | 3 | class Book(models.Model): 4 | name = models.CharField(max_length=200) 5 | pages = models.IntegerField() 6 | 7 | def __str__(self): 8 | return self.name 9 | 10 | 11 | class Tag(models.Model): 12 | book = models.ForeignKey(Book, on_delete=models.CASCADE) 13 | name = models.CharField(max_length=200) 14 | weight = models.IntegerField() 15 | 16 | def __str__(self): 17 | return self.name 18 | 19 | -------------------------------------------------------------------------------- /apps/books_pc_formset/templates/books_pc_formset/book_confirm_delete.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block content %} 4 | {% include "books_pc_formset/nav.html" with book=book title="Delete" %} 5 | 6 |
{% csrf_token %} 7 | Are you sure you want to delete "{{ book }}" ? 8 | 9 |
10 | 11 | {% endblock %} 12 | -------------------------------------------------------------------------------- /apps/books_pc_formset/templates/books_pc_formset/book_form.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block content %} 4 | {% include "books_pc_formset/nav.html" with book=book title="Edit" %} 5 | 6 |
{% csrf_token %} 7 | {{ form.as_p }} 8 | Tags 9 | 10 | {{ formset.as_table }} 11 |
12 | 13 | 14 | 15 | {% endblock %} 16 | -------------------------------------------------------------------------------- /apps/books_pc_formset/templates/books_pc_formset/home.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block content %} 4 | {% include "books_pc_formset/nav.html" %} 5 | 6 | Books Parent/Child Formset 7 | 8 |
    9 | {% for book in books %} 10 |
  • {{ book.name }} ({{ book.pages }} Pages) 11 | edit 12 | delete 13 |
  • 14 | {% endfor %} 15 |
16 | 17 | New 18 | 19 | {% endblock %} 20 | -------------------------------------------------------------------------------- /apps/books_pc_formset/templates/books_pc_formset/nav.html: -------------------------------------------------------------------------------- 1 |

2 | Application Home 3 | {% if book %} 4 | > {{ book }} 5 | {% endif %} 6 | {% if title %} 7 | > {{ title }} 8 | {% endif %} 9 |

-------------------------------------------------------------------------------- /apps/books_pc_formset/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /apps/books_pc_formset/urls.py: -------------------------------------------------------------------------------- 1 | from django.urls import path 2 | 3 | from . import views 4 | 5 | app_name = 'books_pc_formset' 6 | 7 | urlpatterns = [ 8 | path('', views.home, name='home'), 9 | path('new/', views.book_create, name='book_new'), 10 | path('edit//', views.book_update, name='book_edit'), 11 | path('delete//', views.book_delete, name='book_delete'), 12 | ] 13 | -------------------------------------------------------------------------------- /apps/books_pc_formset/views.py: -------------------------------------------------------------------------------- 1 | from django.shortcuts import render, redirect, get_object_or_404 2 | from django.forms.models import inlineformset_factory 3 | 4 | from .models import Book, Tag 5 | from .forms import BookForm 6 | 7 | 8 | def home(request, template_name='books_pc_formset/home.html'): 9 | books = Book.objects.all() 10 | ctx = { 11 | 'books': books, 12 | } 13 | return render(request, template_name, ctx) 14 | 15 | def book_create(request, template_name='books_pc_formset/book_form.html'): 16 | InlineFormSet = inlineformset_factory(Book, Tag, fields=('name', 'weight')) 17 | form = BookForm(request.POST or None) 18 | formset = InlineFormSet(request.POST or None, instance=Book()) 19 | if form.is_valid() and formset.is_valid(): 20 | book = form.save() 21 | formset.instance = book 22 | formset.save() 23 | return redirect('books_pc_formset:home') 24 | ctx = { 25 | 'form': form, 26 | 'formset': formset, 27 | } 28 | return render(request, template_name, ctx) 29 | 30 | def book_update(request, pk, template_name='books_pc_formset/book_form.html'): 31 | InlineFormSet = inlineformset_factory(Book, Tag, fields=('name', 'weight')) 32 | book = get_object_or_404(Book, pk=pk) 33 | form = BookForm(request.POST or None, instance=book) 34 | formset = InlineFormSet(request.POST or None, instance=book) 35 | if form.is_valid() and formset.is_valid(): 36 | book = form.save() 37 | formset.instance = book 38 | formset.save() 39 | return redirect('books_pc_formset:home') 40 | ctx = { 41 | 'form': form, 42 | 'formset': formset, 43 | } 44 | return render(request, template_name, ctx) 45 | 46 | def book_delete(request, pk, template_name='books_pc_formset/book_confirm_delete.html'): 47 | book = get_object_or_404(Book, pk=pk) 48 | if request.method=='POST': 49 | book.delete() 50 | return redirect('books_pc_formset:home') 51 | ctx = { 52 | 'book': book, 53 | } 54 | return render(request, template_name, ctx) 55 | -------------------------------------------------------------------------------- /apps/books_pc_formset2/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rayed/django-crud-parent-child/982f70f1ad762fca52500de09555371f1bcba8ac/apps/books_pc_formset2/__init__.py -------------------------------------------------------------------------------- /apps/books_pc_formset2/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | from books_pc_formset2.models import Book, Person, Contributor 3 | 4 | 5 | class ContributorInline(admin.TabularInline): 6 | model = Contributor 7 | 8 | class BookAdmin(admin.ModelAdmin): 9 | inlines = [ 10 | ContributorInline, 11 | ] 12 | 13 | admin.site.register(Book, BookAdmin) 14 | admin.site.register(Person) 15 | -------------------------------------------------------------------------------- /apps/books_pc_formset2/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class BooksPcFormset2Config(AppConfig): 5 | name = 'books_pc_formset2' 6 | -------------------------------------------------------------------------------- /apps/books_pc_formset2/forms.py: -------------------------------------------------------------------------------- 1 | from django.forms import ModelForm 2 | 3 | from .models import Book, Person 4 | 5 | class BookForm(ModelForm): 6 | class Meta: 7 | model = Book 8 | fields = ['name', 'pages'] 9 | 10 | class PersonForm(ModelForm): 11 | class Meta: 12 | model = Person 13 | fields = ['name', 'email'] -------------------------------------------------------------------------------- /apps/books_pc_formset2/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.13 on 2020-06-25 16:03 2 | 3 | from django.db import migrations, models 4 | import django.db.models.deletion 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | initial = True 10 | 11 | dependencies = [ 12 | ] 13 | 14 | operations = [ 15 | migrations.CreateModel( 16 | name='Book', 17 | fields=[ 18 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 19 | ('name', models.CharField(max_length=200)), 20 | ('pages', models.IntegerField()), 21 | ], 22 | ), 23 | migrations.CreateModel( 24 | name='Person', 25 | fields=[ 26 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 27 | ('name', models.CharField(max_length=200)), 28 | ('email', models.EmailField(max_length=254)), 29 | ], 30 | ), 31 | migrations.CreateModel( 32 | name='Contributor', 33 | fields=[ 34 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 35 | ('contribution', models.IntegerField(choices=[(1, 'Author'), (2, 'Editor'), (3, 'Reviewer')])), 36 | ('book', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='books_pc_formset2.Book')), 37 | ('person', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='books_pc_formset2.Person')), 38 | ], 39 | ), 40 | ] 41 | -------------------------------------------------------------------------------- /apps/books_pc_formset2/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rayed/django-crud-parent-child/982f70f1ad762fca52500de09555371f1bcba8ac/apps/books_pc_formset2/migrations/__init__.py -------------------------------------------------------------------------------- /apps/books_pc_formset2/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | 3 | class Person(models.Model): 4 | name = models.CharField(max_length=200) 5 | email = models.EmailField() 6 | 7 | def __str__(self): 8 | return self.name 9 | 10 | 11 | class Book(models.Model): 12 | name = models.CharField(max_length=200) 13 | pages = models.IntegerField() 14 | 15 | def __str__(self): 16 | return self.name 17 | 18 | 19 | class Contributor(models.Model): 20 | AUTHOR = 1 21 | EDITOR = 2 22 | REVIEWER = 3 23 | CONTRIBUTION_CHOICES = ( 24 | (AUTHOR, "Author"), 25 | (EDITOR, "Editor"), 26 | (REVIEWER, "Reviewer"), 27 | ) 28 | book = models.ForeignKey(Book,on_delete=models.CASCADE) 29 | person = models.ForeignKey(Person,on_delete=models.CASCADE) 30 | contribution = models.IntegerField(choices=CONTRIBUTION_CHOICES) 31 | 32 | def __str__(self): 33 | return "%s %s" % (self.contribution, self.person) 34 | 35 | -------------------------------------------------------------------------------- /apps/books_pc_formset2/templates/books_pc_formset2/book_confirm_delete.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block content %} 4 | {% include "books_pc_formset2/nav.html" with book=book title="Delete" %} 5 | 6 |
{% csrf_token %} 7 | Are you sure you want to delete "{{ book }}" ? 8 | 9 |
10 | 11 | {% endblock %} 12 | -------------------------------------------------------------------------------- /apps/books_pc_formset2/templates/books_pc_formset2/book_form.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block content %} 4 | {% include "books_pc_formset2/nav.html" with book=book title="Edit Book" %} 5 | 6 |
{% csrf_token %} 7 | {{ form.as_p }} 8 | Contributors 9 | 10 | {{ formset.as_table }} 11 |
12 | 13 | 14 | 15 | {% endblock %} 16 | -------------------------------------------------------------------------------- /apps/books_pc_formset2/templates/books_pc_formset2/home.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block content %} 4 | {% include "books_pc_formset2/nav.html" %} 5 | 6 |

Books Parent/Child Formset2

7 | 8 | 9 |

People

10 |
    11 | {% for person in persons %} 12 |
  • {{ person.name }} ({{ person.email }}) 13 | edit 14 | delete 15 |
  • 16 | {% endfor %} 17 |
18 | 19 | New 20 | 21 | 22 |

Books

23 |
    24 | {% for book in books %} 25 |
  • {{ book.name }} ({{ book.pages }} Pages) 26 | edit 27 | delete 28 |
  • 29 | {% endfor %} 30 |
31 | 32 | New 33 | 34 | {% endblock %} 35 | -------------------------------------------------------------------------------- /apps/books_pc_formset2/templates/books_pc_formset2/nav.html: -------------------------------------------------------------------------------- 1 |

2 | Application Home 3 | {% if book %} 4 | > {{ book }} 5 | {% endif %} 6 | {% if title %} 7 | > {{ title }} 8 | {% endif %} 9 |

-------------------------------------------------------------------------------- /apps/books_pc_formset2/templates/books_pc_formset2/person_confirm_delete.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block content %} 4 | {% include "books_pc_formset2/nav.html" with person=person title="Delete" %} 5 | 6 |
{% csrf_token %} 7 | Are you sure you want to delete "{{ person }}" ? 8 | 9 |
10 | 11 | {% endblock %} 12 | -------------------------------------------------------------------------------- /apps/books_pc_formset2/templates/books_pc_formset2/person_form.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block content %} 4 | {% include "books_pc_formset2/nav.html" with person=person title="Edit Person" %} 5 | 6 |
{% csrf_token %} 7 | {{ form.as_p }} 8 | 9 |
10 | 11 | {% endblock %} 12 | -------------------------------------------------------------------------------- /apps/books_pc_formset2/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /apps/books_pc_formset2/urls.py: -------------------------------------------------------------------------------- 1 | from django.urls import path 2 | 3 | from . import views 4 | 5 | app_name = 'books_pc_formset2' 6 | 7 | urlpatterns = [ 8 | path('', views.home, name='home'), 9 | 10 | path('person_new', views.person_create, name='person_new'), 11 | path('person_edit/', views.person_update, name='person_edit'), 12 | path('person_delete/', views.person_delete, name='person_delete'), 13 | 14 | path('book_new', views.book_create, name='book_new'), 15 | path('book_edit/', views.book_update, name='book_edit'), 16 | path('book_delete/', views.book_delete, name='book_delete'), 17 | ] 18 | -------------------------------------------------------------------------------- /apps/books_pc_formset2/views.py: -------------------------------------------------------------------------------- 1 | from django.shortcuts import render, redirect, get_object_or_404 2 | from django.forms.models import inlineformset_factory 3 | 4 | from .models import Book, Contributor, Person 5 | from .forms import BookForm, PersonForm 6 | 7 | 8 | 9 | # ============ Home =============== 10 | 11 | def home(request, template_name='books_pc_formset2/home.html'): 12 | books = Book.objects.all() 13 | persons = Person.objects.all() 14 | ctx = { 15 | 'books': books, 16 | 'persons': persons, 17 | } 18 | return render(request, template_name, ctx) 19 | 20 | 21 | # ============ Book CRUD =============== 22 | 23 | def book_create(request, template_name='books_pc_formset2/book_form.html'): 24 | InlineFormSet = inlineformset_factory(Book, Contributor, fields=('person', 'contribution')) 25 | form = BookForm(request.POST or None) 26 | formset = InlineFormSet(request.POST or None, instance=Book()) 27 | if form.is_valid() and formset.is_valid(): 28 | book = form.save() 29 | formset.instance = book 30 | formset.save() 31 | return redirect('books_pc_formset2:home') 32 | ctx = { 33 | 'form': form, 34 | 'formset': formset, 35 | } 36 | return render(request, template_name, ctx) 37 | 38 | def book_update(request, pk, template_name='books_pc_formset2/book_form.html'): 39 | InlineFormSet = inlineformset_factory(Book, Contributor, fields=('person', 'contribution')) 40 | book= get_object_or_404(Book, pk=pk) 41 | form = BookForm(request.POST or None, instance=book) 42 | formset = InlineFormSet(request.POST or None, instance=book) 43 | if form.is_valid() and formset.is_valid(): 44 | book = form.save() 45 | formset.instance = book 46 | formset.save() 47 | return redirect('books_pc_formset2:home') 48 | ctx = { 49 | 'form': form, 50 | 'formset': formset, 51 | } 52 | return render(request, template_name, ctx) 53 | 54 | def book_delete(request, pk, template_name='books_pc_formset2/book_confirm_delete.html'): 55 | book= get_object_or_404(Book, pk=pk) 56 | if request.method=='POST': 57 | book.delete() 58 | return redirect('books_pc_formset2:home') 59 | ctx = { 60 | 'book': book, 61 | } 62 | return render(request, template_name, ctx) 63 | 64 | 65 | # ============ Person CRUD =============== 66 | 67 | def person_create(request, template_name='books_pc_formset2/person_form.html'): 68 | form = PersonForm(request.POST or None) 69 | if form.is_valid(): 70 | form.save() 71 | return redirect('books_pc_formset2:home') 72 | ctx = { 73 | 'form': form, 74 | } 75 | return render(request, template_name, ctx) 76 | 77 | def person_update(request, pk, template_name='books_pc_formset2/person_form.html'): 78 | person = get_object_or_404(Person, pk=pk) 79 | form = PersonForm(request.POST or None, instance=person) 80 | if form.is_valid(): 81 | form.save() 82 | return redirect('books_pc_formset2:home') 83 | ctx = { 84 | 'form': form, 85 | } 86 | return render(request, template_name, ctx) 87 | 88 | def person_delete(request, pk, template_name='books_pc_formset2/person_confirm_delete.html'): 89 | person= get_object_or_404(Person, pk=pk) 90 | if request.method=='POST': 91 | person.delete() 92 | return redirect('books_pc_formset2:home') 93 | ctx = { 94 | 'person': person, 95 | } 96 | return render(request, template_name, ctx) 97 | -------------------------------------------------------------------------------- /apps/books_pc_multi_view/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rayed/django-crud-parent-child/982f70f1ad762fca52500de09555371f1bcba8ac/apps/books_pc_multi_view/__init__.py -------------------------------------------------------------------------------- /apps/books_pc_multi_view/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | from books_pc_multi_view.models import Book, Review 3 | 4 | admin.site.register(Book) 5 | admin.site.register(Review) -------------------------------------------------------------------------------- /apps/books_pc_multi_view/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class BooksPcMultiViewConfig(AppConfig): 5 | name = 'books_pc_multi_view' 6 | -------------------------------------------------------------------------------- /apps/books_pc_multi_view/forms.py: -------------------------------------------------------------------------------- 1 | from django.forms import ModelForm 2 | 3 | from .models import Book, Review 4 | 5 | class BookForm(ModelForm): 6 | class Meta: 7 | model = Book 8 | fields = ['name', 'pages'] 9 | 10 | class ReviewForm(ModelForm): 11 | class Meta: 12 | model = Review 13 | fields = ['name', 'review'] -------------------------------------------------------------------------------- /apps/books_pc_multi_view/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.13 on 2020-06-25 16:03 2 | 3 | from django.db import migrations, models 4 | import django.db.models.deletion 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | initial = True 10 | 11 | dependencies = [ 12 | ] 13 | 14 | operations = [ 15 | migrations.CreateModel( 16 | name='Book', 17 | fields=[ 18 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 19 | ('name', models.CharField(max_length=200)), 20 | ('pages', models.IntegerField()), 21 | ], 22 | ), 23 | migrations.CreateModel( 24 | name='Review', 25 | fields=[ 26 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 27 | ('name', models.CharField(max_length=200)), 28 | ('review', models.TextField()), 29 | ('book', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='books_pc_multi_view.Book')), 30 | ], 31 | ), 32 | ] 33 | -------------------------------------------------------------------------------- /apps/books_pc_multi_view/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rayed/django-crud-parent-child/982f70f1ad762fca52500de09555371f1bcba8ac/apps/books_pc_multi_view/migrations/__init__.py -------------------------------------------------------------------------------- /apps/books_pc_multi_view/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | 3 | 4 | class Book(models.Model): 5 | name = models.CharField(max_length=200) 6 | pages = models.IntegerField() 7 | 8 | def __str__(self): 9 | return self.name 10 | 11 | 12 | class Review(models.Model): 13 | book = models.ForeignKey(Book,on_delete=models.CASCADE) 14 | name = models.CharField(max_length=200) 15 | review = models.TextField() 16 | 17 | def __str__(self): 18 | return "Review from: " + self.name 19 | -------------------------------------------------------------------------------- /apps/books_pc_multi_view/templates/books_pc_multi_view/book_confirm_delete.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block content %} 4 | {% include "books_pc_multi_view/nav.html" with book=book title="Delete" %} 5 | 6 |
{% csrf_token %} 7 | Are you sure you want to delete "{{ object }}" ? 8 | 9 |
10 | {% endblock %} 11 | -------------------------------------------------------------------------------- /apps/books_pc_multi_view/templates/books_pc_multi_view/book_form.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | 4 | {% block content %} 5 | {% include "books_pc_multi_view/nav.html" with book=book title="Edit" %} 6 |
{% csrf_token %} 7 | {{ form.as_p }} 8 | 9 |
10 | {% endblock %} 11 | -------------------------------------------------------------------------------- /apps/books_pc_multi_view/templates/books_pc_multi_view/book_view.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | 4 | {% block content %} 5 | {% include "books_pc_multi_view/nav.html" with book=book title="View" %} 6 | 7 | Pages: {{ book.pages }} 8 | 9 |

Reviews

10 |
    11 | {% for review in reviews %} 12 |
  • 13 | {{ review.name }}
    14 | {{ review.review|linebreaks }} 15 |
    16 | edit 17 | delete 18 |
  • 19 | {% endfor %} 20 |
21 | 22 | New Review 23 | {% endblock %} 24 | -------------------------------------------------------------------------------- /apps/books_pc_multi_view/templates/books_pc_multi_view/home.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block title %}Book List{% endblock %} 4 | 5 | {% block content %} 6 | {% include "books_pc_multi_view/nav.html" %} 7 | 8 | Books Parent/Child with Multi View 9 | 10 |
    11 | {% for book in books %} 12 |
  • {{ book.name }} ({{ book.pages }} Pages) 13 | view 14 | edit 15 | delete 16 |
  • 17 | {% endfor %} 18 |
19 | 20 | New 21 | {% endblock %} 22 | -------------------------------------------------------------------------------- /apps/books_pc_multi_view/templates/books_pc_multi_view/nav.html: -------------------------------------------------------------------------------- 1 |

2 | Application Home 3 | {% if book %} 4 | > {{ book.name }} 5 | {% endif %} 6 | {% if title %} 7 | > {{ title }} 8 | {% endif %} 9 |

-------------------------------------------------------------------------------- /apps/books_pc_multi_view/templates/books_pc_multi_view/review_confirm_delete.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | 4 | {% block content %} 5 | {% include "books_pc_multi_view/nav.html" with book=book title="Delete Review" %} 6 |
{% csrf_token %} 7 | Are you sure you want to delete "{{ object }}" ? 8 | 9 |
10 | {% endblock %} 11 | -------------------------------------------------------------------------------- /apps/books_pc_multi_view/templates/books_pc_multi_view/review_form.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | 4 | {% block content %} 5 | {% include "books_pc_multi_view/nav.html" with book=book title="Edit Review" %} 6 |
{% csrf_token %} 7 | {{ form.as_p }} 8 | 9 |
10 | {% endblock %} 11 | -------------------------------------------------------------------------------- /apps/books_pc_multi_view/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /apps/books_pc_multi_view/urls.py: -------------------------------------------------------------------------------- 1 | from django.urls import path 2 | 3 | from . import views 4 | 5 | app_name = 'books_pc_multi_view' 6 | 7 | urlpatterns = [ 8 | path('', views.home, name='home'), 9 | 10 | path('book_view//', views.book_view, name='book_view'), 11 | path('book_new', views.book_create, name='book_new'), 12 | path('book_edit//', views.book_update, name='book_edit'), 13 | path('book_delete//', views.book_delete, name='book_delete'), 14 | 15 | path('review_new//', views.review_create, name='review_new'), 16 | path('review_edit//', views.review_update, name='review_edit'), 17 | path('review_delete//', views.review_delete, name='review_delete'), 18 | ] 19 | -------------------------------------------------------------------------------- /apps/books_pc_multi_view/views.py: -------------------------------------------------------------------------------- 1 | from django.shortcuts import render, redirect, get_object_or_404 2 | from django.forms import ModelForm 3 | 4 | from .models import Book, Review 5 | from .forms import BookForm, ReviewForm 6 | 7 | 8 | # ========== Home ========= 9 | 10 | def home(request, template_name='books_pc_multi_view/home.html'): 11 | books = Book.objects.all() 12 | ctx = { 13 | 'books': books, 14 | } 15 | return render(request, template_name, ctx) 16 | 17 | 18 | # ========== Book CRUD ========= 19 | 20 | def book_view(request, pk, template_name='books_pc_multi_view/book_view.html'): 21 | book= get_object_or_404(Book, pk=pk) 22 | reviews = Review.objects.filter(book=book) 23 | ctx = { 24 | 'book': book, 25 | 'reviews': reviews, 26 | } 27 | return render(request, template_name, ctx) 28 | 29 | def book_create(request, template_name='books_pc_multi_view/book_form.html'): 30 | form = BookForm(request.POST or None) 31 | if form.is_valid(): 32 | form.save() 33 | return redirect('books_pc_multi_view:home') 34 | ctx = { 35 | 'form': form, 36 | } 37 | return render(request, template_name, ctx) 38 | 39 | def book_update(request, pk, template_name='books_pc_multi_view/book_form.html'): 40 | book= get_object_or_404(Book, pk=pk) 41 | form = BookForm(request.POST or None, instance=book) 42 | if form.is_valid(): 43 | form.save() 44 | return redirect('books_pc_multi_view:home') 45 | ctx = { 46 | 'form': form, 47 | 'book': book, 48 | } 49 | return render(request, template_name, ctx) 50 | 51 | def book_delete(request, pk, template_name='books_pc_multi_view/book_confirm_delete.html'): 52 | book= get_object_or_404(Book, pk=pk) 53 | if request.method=='POST': 54 | book.delete() 55 | return redirect('books_pc_multi_view:home') 56 | ctx = { 57 | 'object': book, 58 | 'book': book, 59 | } 60 | return render(request, template_name, ctx) 61 | 62 | 63 | # ========== Review CRUD ========= 64 | 65 | def review_create(request, parent_pk, template_name='books_pc_multi_view/review_form.html'): 66 | book = get_object_or_404(Book, pk=parent_pk) 67 | form = ReviewForm(request.POST or None) 68 | if form.is_valid(): 69 | review = form.save(commit=False) 70 | review.book = book 71 | review.save() 72 | return redirect('books_pc_multi_view:book_view', parent_pk) 73 | ctx = { 74 | 'form': form, 75 | 'book': book, 76 | } 77 | return render(request, template_name, ctx) 78 | 79 | def review_update(request, pk, template_name='books_pc_multi_view/review_form.html'): 80 | review = get_object_or_404(Review, pk=pk) 81 | parent_pk = review.book.pk 82 | form = ReviewForm(request.POST or None, instance=review) 83 | if form.is_valid(): 84 | form.save() 85 | return redirect('books_pc_multi_view:book_view', parent_pk) 86 | ctx = { 87 | 'form': form, 88 | 'book': review.book, 89 | } 90 | return render(request, template_name, ctx) 91 | 92 | def review_delete(request, pk, template_name='books_pc_multi_view/review_confirm_delete.html'): 93 | review = get_object_or_404(Review, pk=pk) 94 | parent_pk = review.book.pk 95 | if request.method=='POST': 96 | review.delete() 97 | return redirect('books_pc_multi_view:book_view', parent_pk) 98 | ctx = { 99 | 'object': review, 100 | 'book': review.book, 101 | } 102 | return render(request, template_name, ctx) 103 | -------------------------------------------------------------------------------- /apps/books_pc_multi_view2/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rayed/django-crud-parent-child/982f70f1ad762fca52500de09555371f1bcba8ac/apps/books_pc_multi_view2/__init__.py -------------------------------------------------------------------------------- /apps/books_pc_multi_view2/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | from books_pc_multi_view2.models import Person,Book, Review 3 | 4 | admin.site.register(Person) 5 | admin.site.register(Book) 6 | admin.site.register(Review) 7 | -------------------------------------------------------------------------------- /apps/books_pc_multi_view2/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class BooksPcMultiView2Config(AppConfig): 5 | name = 'books_pc_multi_view2' 6 | -------------------------------------------------------------------------------- /apps/books_pc_multi_view2/forms.py: -------------------------------------------------------------------------------- 1 | from django.forms import ModelForm 2 | 3 | from .models import Person, Book, Review 4 | 5 | class PersonForm(ModelForm): 6 | class Meta: 7 | model = Person 8 | fields = ['name', 'email'] 9 | 10 | class BookForm(ModelForm): 11 | class Meta: 12 | model = Book 13 | fields = ['name', 'pages'] 14 | 15 | class ReviewForm(ModelForm): 16 | class Meta: 17 | model = Review 18 | fields = ['person', 'review'] -------------------------------------------------------------------------------- /apps/books_pc_multi_view2/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.13 on 2020-06-25 16:03 2 | 3 | from django.db import migrations, models 4 | import django.db.models.deletion 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | initial = True 10 | 11 | dependencies = [ 12 | ] 13 | 14 | operations = [ 15 | migrations.CreateModel( 16 | name='Book', 17 | fields=[ 18 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 19 | ('name', models.CharField(max_length=200)), 20 | ('pages', models.IntegerField()), 21 | ], 22 | ), 23 | migrations.CreateModel( 24 | name='Person', 25 | fields=[ 26 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 27 | ('name', models.CharField(max_length=200)), 28 | ('email', models.EmailField(max_length=254)), 29 | ], 30 | ), 31 | migrations.CreateModel( 32 | name='Review', 33 | fields=[ 34 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 35 | ('review', models.TextField()), 36 | ('book', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='books_pc_multi_view2.Book')), 37 | ('person', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='books_pc_multi_view2.Person')), 38 | ], 39 | ), 40 | ] 41 | -------------------------------------------------------------------------------- /apps/books_pc_multi_view2/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rayed/django-crud-parent-child/982f70f1ad762fca52500de09555371f1bcba8ac/apps/books_pc_multi_view2/migrations/__init__.py -------------------------------------------------------------------------------- /apps/books_pc_multi_view2/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | 3 | 4 | class Person(models.Model): 5 | name = models.CharField(max_length=200) 6 | email = models.EmailField() 7 | 8 | def __str__(self): 9 | return self.name 10 | 11 | 12 | class Book(models.Model): 13 | name = models.CharField(max_length=200) 14 | pages = models.IntegerField() 15 | 16 | def __str__(self): 17 | return self.name 18 | 19 | 20 | class Review(models.Model): 21 | book = models.ForeignKey(Book,on_delete=models.CASCADE) 22 | person = models.ForeignKey(Person,on_delete=models.CASCADE) 23 | review = models.TextField() 24 | 25 | def __str__(self): 26 | return "Review from: " + self.person.name 27 | -------------------------------------------------------------------------------- /apps/books_pc_multi_view2/templates/books_pc_multi_view2/book_confirm_delete.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block content %} 4 | {% include "books_pc_multi_view2/nav.html" with book=book title="Delete" %} 5 | 6 |
{% csrf_token %} 7 | Are you sure you want to delete "{{ object }}" ? 8 | 9 |
10 | {% endblock %} 11 | -------------------------------------------------------------------------------- /apps/books_pc_multi_view2/templates/books_pc_multi_view2/book_form.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | 4 | {% block content %} 5 | {% include "books_pc_multi_view2/nav.html" with book=book title="Edit" %} 6 |
{% csrf_token %} 7 | {{ form.as_p }} 8 | 9 |
10 | {% endblock %} 11 | -------------------------------------------------------------------------------- /apps/books_pc_multi_view2/templates/books_pc_multi_view2/book_view.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | 4 | {% block content %} 5 | {% include "books_pc_multi_view2/nav.html" with book=book title="View" %} 6 | 7 | Pages: {{ book.pages }} 8 | 9 |

Reviews

10 |
    11 | {% for review in reviews %} 12 |
  • 13 | {{ review.person }}
    14 | {{ review.review|linebreaks }} 15 |
    16 | edit 17 | delete 18 |
  • 19 | {% endfor %} 20 |
21 | 22 | New Review 23 | {% endblock %} 24 | -------------------------------------------------------------------------------- /apps/books_pc_multi_view2/templates/books_pc_multi_view2/home.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block title %}Book List{% endblock %} 4 | 5 | {% block content %} 6 | {% include "books_pc_multi_view2/nav.html" %} 7 | 8 | Books Parent/Child with Multi View and Foreign Key 9 | 10 |

People

11 |
    12 | {% for person in persons %} 13 |
  • {{ person.name }} ({{ person.email }}) 14 | edit 15 | delete 16 |
  • 17 | {% endfor %} 18 |
19 | 20 | New 21 | 22 | 23 |

Books

24 |
    25 | {% for book in books %} 26 |
  • {{ book.name }} ({{ book.pages }} Pages) 27 | view 28 | edit 29 | delete 30 |
  • 31 | {% endfor %} 32 |
33 | 34 | New 35 | {% endblock %} 36 | -------------------------------------------------------------------------------- /apps/books_pc_multi_view2/templates/books_pc_multi_view2/nav.html: -------------------------------------------------------------------------------- 1 |

2 | Application Home 3 | {% if book %} 4 | > {{ book.name }} 5 | {% endif %} 6 | {% if title %} 7 | > {{ title }} 8 | {% endif %} 9 |

-------------------------------------------------------------------------------- /apps/books_pc_multi_view2/templates/books_pc_multi_view2/person_confirm_delete.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block content %} 4 | {% include "books_pc_multi_view2/nav.html" with person=person title="Delete" %} 5 | 6 |
{% csrf_token %} 7 | Are you sure you want to delete "{{ person }}" ? 8 | 9 |
10 | 11 | {% endblock %} 12 | -------------------------------------------------------------------------------- /apps/books_pc_multi_view2/templates/books_pc_multi_view2/person_form.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block content %} 4 | {% include "books_pc_multi_view2/nav.html" with person=person title="Edit Person" %} 5 | 6 |
{% csrf_token %} 7 | {{ form.as_p }} 8 | 9 |
10 | 11 | {% endblock %} 12 | -------------------------------------------------------------------------------- /apps/books_pc_multi_view2/templates/books_pc_multi_view2/review_confirm_delete.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | 4 | {% block content %} 5 | {% include "books_pc_multi_view2/nav.html" with book=book title="Delete Review" %} 6 |
{% csrf_token %} 7 | Are you sure you want to delete "{{ object }}" ? 8 | 9 |
10 | {% endblock %} 11 | -------------------------------------------------------------------------------- /apps/books_pc_multi_view2/templates/books_pc_multi_view2/review_form.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | 4 | {% block content %} 5 | {% include "books_pc_multi_view2/nav.html" with book=book title="Edit Review" %} 6 |
{% csrf_token %} 7 | {{ form.as_p }} 8 | 9 |
10 | {% endblock %} 11 | -------------------------------------------------------------------------------- /apps/books_pc_multi_view2/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /apps/books_pc_multi_view2/urls.py: -------------------------------------------------------------------------------- 1 | from django.urls import path 2 | 3 | from . import views 4 | 5 | app_name = 'books_pc_multi_view2' 6 | 7 | urlpatterns = [ 8 | path('', views.home, name='home'), 9 | 10 | path('person_new', views.person_create, name='person_new'), 11 | path('person_edit//', views.person_update, name='person_edit'), 12 | path('person_delete//', views.person_delete, name='person_delete'), 13 | 14 | path('book_view//', views.book_view, name='book_view'), 15 | path('book_new', views.book_create, name='book_new'), 16 | path('book_edit//', views.book_update, name='book_edit'), 17 | path('book_delete//', views.book_delete, name='book_delete'), 18 | 19 | path('review_new//', views.review_create, name='review_new'), 20 | path('review_edit//', views.review_update, name='review_edit'), 21 | path('review_delete//', views.review_delete, name='review_delete'), 22 | ] 23 | -------------------------------------------------------------------------------- /apps/books_pc_multi_view2/views.py: -------------------------------------------------------------------------------- 1 | from django.shortcuts import render, redirect, get_object_or_404 2 | 3 | from .models import Person, Book, Review 4 | from .forms import PersonForm, BookForm, ReviewForm 5 | 6 | 7 | # ========== Home ========= 8 | 9 | def home(request, template_name='books_pc_multi_view2/home.html'): 10 | books = Book.objects.all() 11 | persons = Person.objects.all() 12 | ctx = { 13 | 'books': books, 14 | 'persons': persons, 15 | } 16 | return render(request, template_name, ctx) 17 | 18 | 19 | # ========== Book CRUD ========= 20 | 21 | def book_view(request, pk, template_name='books_pc_multi_view2/book_view.html'): 22 | book= get_object_or_404(Book, pk=pk) 23 | reviews = Review.objects.filter(book=book) 24 | ctx = { 25 | 'book': book, 26 | 'reviews': reviews, 27 | } 28 | return render(request, template_name, ctx) 29 | 30 | def book_create(request, template_name='books_pc_multi_view2/book_form.html'): 31 | form = BookForm(request.POST or None) 32 | if form.is_valid(): 33 | form.save() 34 | return redirect('books_pc_multi_view2:home') 35 | ctx = { 36 | 'form': form, 37 | } 38 | return render(request, template_name, ctx) 39 | 40 | def book_update(request, pk, template_name='books_pc_multi_view2/book_form.html'): 41 | book= get_object_or_404(Book, pk=pk) 42 | form = BookForm(request.POST or None, instance=book) 43 | if form.is_valid(): 44 | form.save() 45 | return redirect('books_pc_multi_view2:home') 46 | ctx = { 47 | 'form': form, 48 | 'book': book, 49 | } 50 | return render(request, template_name, ctx) 51 | 52 | def book_delete(request, pk, template_name='books_pc_multi_view2/book_confirm_delete.html'): 53 | book= get_object_or_404(Book, pk=pk) 54 | if request.method=='POST': 55 | book.delete() 56 | return redirect('books_pc_multi_view2:home') 57 | ctx = { 58 | 'object': book, 59 | 'book': book, 60 | } 61 | return render(request, template_name, ctx) 62 | 63 | 64 | # ========== Review CRUD ========= 65 | 66 | def review_create(request, parent_pk, template_name='books_pc_multi_view2/review_form.html'): 67 | book = get_object_or_404(Book, pk=parent_pk) 68 | form = ReviewForm(request.POST or None) 69 | if form.is_valid(): 70 | review = form.save(commit=False) 71 | review.book = book 72 | review.save() 73 | return redirect('books_pc_multi_view2:book_view', parent_pk) 74 | ctx = { 75 | 'form': form, 76 | 'book': book, 77 | } 78 | return render(request, template_name, ctx) 79 | 80 | def review_update(request, pk, template_name='books_pc_multi_view2/review_form.html'): 81 | review = get_object_or_404(Review, pk=pk) 82 | parent_pk = review.book.pk 83 | form = ReviewForm(request.POST or None, instance=review) 84 | if form.is_valid(): 85 | form.save() 86 | return redirect('books_pc_multi_view2:book_view', parent_pk) 87 | ctx = { 88 | 'form': form, 89 | 'book': review.book, 90 | } 91 | return render(request, template_name, ctx) 92 | 93 | def review_delete(request, pk, template_name='books_pc_multi_view2/review_confirm_delete.html'): 94 | review = get_object_or_404(Review, pk=pk) 95 | parent_pk = review.book.pk 96 | if request.method=='POST': 97 | review.delete() 98 | return redirect('books_pc_multi_view2:book_view', parent_pk) 99 | ctx = { 100 | 'object': review, 101 | 'book': review.book, 102 | } 103 | return render(request, template_name, ctx) 104 | 105 | 106 | # ============ Person CRUD =============== 107 | 108 | def person_create(request, template_name='books_pc_multi_view2/person_form.html'): 109 | form = PersonForm(request.POST or None) 110 | if form.is_valid(): 111 | form.save() 112 | return redirect('books_pc_multi_view2:home') 113 | ctx = { 114 | 'form': form, 115 | } 116 | return render(request, template_name, ctx) 117 | 118 | def person_update(request, pk, template_name='books_pc_multi_view2/person_form.html'): 119 | person = get_object_or_404(Person, pk=pk) 120 | form = PersonForm(request.POST or None, instance=person) 121 | if form.is_valid(): 122 | form.save() 123 | return redirect('books_pc_multi_view2:home') 124 | ctx = { 125 | 'form': form, 126 | } 127 | return render(request, template_name, ctx) 128 | 129 | def person_delete(request, pk, template_name='books_pc_multi_view2/person_confirm_delete.html'): 130 | person= get_object_or_404(Person, pk=pk) 131 | if request.method=='POST': 132 | person.delete() 133 | return redirect('books_pc_multi_view2:home') 134 | ctx = { 135 | 'person': person, 136 | } 137 | return render(request, template_name, ctx) 138 | -------------------------------------------------------------------------------- /apps/books_simple/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rayed/django-crud-parent-child/982f70f1ad762fca52500de09555371f1bcba8ac/apps/books_simple/__init__.py -------------------------------------------------------------------------------- /apps/books_simple/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | from books_simple.models import Book 3 | 4 | admin.site.register(Book) -------------------------------------------------------------------------------- /apps/books_simple/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class BooksSimpleConfig(AppConfig): 5 | name = 'books_simple' 6 | -------------------------------------------------------------------------------- /apps/books_simple/forms.py: -------------------------------------------------------------------------------- 1 | from django.forms import ModelForm 2 | 3 | from .models import Book 4 | 5 | class BookForm(ModelForm): 6 | class Meta: 7 | model = Book 8 | fields = ['name', 'pages'] 9 | 10 | -------------------------------------------------------------------------------- /apps/books_simple/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.13 on 2020-06-25 16:03 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | initial = True 9 | 10 | dependencies = [ 11 | ] 12 | 13 | operations = [ 14 | migrations.CreateModel( 15 | name='Book', 16 | fields=[ 17 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 18 | ('name', models.CharField(max_length=200)), 19 | ('pages', models.IntegerField()), 20 | ], 21 | ), 22 | ] 23 | -------------------------------------------------------------------------------- /apps/books_simple/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rayed/django-crud-parent-child/982f70f1ad762fca52500de09555371f1bcba8ac/apps/books_simple/migrations/__init__.py -------------------------------------------------------------------------------- /apps/books_simple/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | 3 | class Book(models.Model): 4 | name = models.CharField(max_length=200) 5 | pages = models.IntegerField() 6 | 7 | def __str__(self): 8 | return self.name 9 | -------------------------------------------------------------------------------- /apps/books_simple/templates/books_simple/book_confirm_delete.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block content %} 4 | {% include "books_simple/nav.html" with book=book title="Delete" %} 5 | 6 |
{% csrf_token %} 7 | Are you sure you want to delete "{{ book }}" ? 8 | 9 |
10 | 11 | {% endblock %} 12 | -------------------------------------------------------------------------------- /apps/books_simple/templates/books_simple/book_form.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block content %} 4 | {% include "books_simple/nav.html" with book=book title="Edit" %} 5 | 6 |
{% csrf_token %} 7 | {{ form.as_p }} 8 | 9 |
10 | 11 | {% endblock %} 12 | -------------------------------------------------------------------------------- /apps/books_simple/templates/books_simple/home.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block content %} 4 | {% include "books_simple/nav.html" with book=book %} 5 | 6 |
    7 | {% for book in books %} 8 |
  • {{ book.name }} ({{ book.pages }} Pages) 9 | edit 10 | delete 11 |
  • 12 | {% endfor %} 13 |
14 | 15 | New 16 | {% endblock %} 17 | -------------------------------------------------------------------------------- /apps/books_simple/templates/books_simple/nav.html: -------------------------------------------------------------------------------- 1 |

2 | Application Home 3 | {% if book %} 4 | > {{ book }} 5 | {% endif %} 6 | {% if title %} 7 | > {{ title }} 8 | {% endif %} 9 |

-------------------------------------------------------------------------------- /apps/books_simple/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /apps/books_simple/urls.py: -------------------------------------------------------------------------------- 1 | from django.urls import path 2 | 3 | from . import views 4 | 5 | app_name = 'books_simple' 6 | 7 | urlpatterns = [ 8 | path('', views.home, name='home'), 9 | path('new/', views.book_create, name='book_new'), 10 | path('edit//', views.book_update, name='book_edit'), 11 | path('delete//', views.book_delete, name='book_delete'), 12 | ] 13 | -------------------------------------------------------------------------------- /apps/books_simple/views.py: -------------------------------------------------------------------------------- 1 | from django.shortcuts import render, redirect, get_object_or_404 2 | 3 | from .models import Book 4 | from .forms import BookForm 5 | 6 | def home(request, template_name='books_simple/home.html'): 7 | books = Book.objects.all() 8 | ctx = { 9 | 'books': books, 10 | } 11 | return render(request, template_name, ctx) 12 | 13 | def book_create(request, template_name='books_simple/book_form.html'): 14 | form = BookForm(request.POST or None) 15 | if form.is_valid(): 16 | form.save() 17 | return redirect('books_simple:home') 18 | ctx = { 19 | 'form': form, 20 | } 21 | return render(request, template_name, ctx) 22 | 23 | def book_update(request, pk, template_name='books_simple/book_form.html'): 24 | book = get_object_or_404(Book, pk=pk) 25 | form = BookForm(request.POST or None, instance=book) 26 | if form.is_valid(): 27 | form.save() 28 | return redirect('books_simple:home') 29 | ctx = { 30 | 'form': form, 31 | 'book': book, 32 | } 33 | return render(request, template_name, ctx) 34 | 35 | def book_delete(request, pk, template_name='books_simple/book_confirm_delete.html'): 36 | book = get_object_or_404(Book, pk=pk) 37 | if request.method=='POST': 38 | book.delete() 39 | return redirect('books_simple:home') 40 | ctx = { 41 | 'book': book, 42 | } 43 | return render(request, template_name, ctx) 44 | -------------------------------------------------------------------------------- /apps/manage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """Django's command-line utility for administrative tasks.""" 3 | import os 4 | import sys 5 | 6 | 7 | def main(): 8 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'apps.settings') 9 | try: 10 | from django.core.management import execute_from_command_line 11 | except ImportError as exc: 12 | raise ImportError( 13 | "Couldn't import Django. Are you sure it's installed and " 14 | "available on your PYTHONPATH environment variable? Did you " 15 | "forget to activate a virtual environment?" 16 | ) from exc 17 | execute_from_command_line(sys.argv) 18 | 19 | 20 | if __name__ == '__main__': 21 | main() 22 | -------------------------------------------------------------------------------- /apps/theme/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rayed/django-crud-parent-child/982f70f1ad762fca52500de09555371f1bcba8ac/apps/theme/__init__.py -------------------------------------------------------------------------------- /apps/theme/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | 3 | # Register your models here. 4 | -------------------------------------------------------------------------------- /apps/theme/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class ThemeConfig(AppConfig): 5 | name = 'theme' 6 | -------------------------------------------------------------------------------- /apps/theme/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rayed/django-crud-parent-child/982f70f1ad762fca52500de09555371f1bcba8ac/apps/theme/migrations/__init__.py -------------------------------------------------------------------------------- /apps/theme/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | 3 | # Create your models here. 4 | -------------------------------------------------------------------------------- /apps/theme/static/css/style.css: -------------------------------------------------------------------------------- 1 | * { 2 | font-family:arial; 3 | } 4 | pre { 5 | font-family:'Courier New', Courier, monospace; 6 | } 7 | a:visited { 8 | color:navy; 9 | } 10 | 11 | #wrapper { 12 | width:960px; 13 | margin:auto; 14 | } -------------------------------------------------------------------------------- /apps/theme/templates/base.html: -------------------------------------------------------------------------------- 1 | {% load staticfiles %} 2 | 3 | 4 | 5 | Django CRUD: {% block title %}Welcome{% endblock %} 6 | 7 | 8 | 9 | 10 |
11 |

Django CRUD Parent/Child Edition

12 | 13 |
14 | {% block content %}{% endblock %} 15 |
16 |
17 | 18 | 19 | -------------------------------------------------------------------------------- /apps/theme/templates/home.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block content %} 4 | 5 | 12 | 13 | You can also access Django built-in Admin interface, you would need to 14 | create a user using the following command: 15 | 16 |
./manage.py  createsuperuser
17 | {% endblock %} 18 | -------------------------------------------------------------------------------- /apps/theme/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /apps/theme/views.py: -------------------------------------------------------------------------------- 1 | from django.shortcuts import render 2 | 3 | def home(request): 4 | return render(request, 'home.html') 5 | 6 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | Django>=3.2.7,<4.0 -------------------------------------------------------------------------------- /www/media/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore -------------------------------------------------------------------------------- /www/static/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore --------------------------------------------------------------------------------