diff --git a/README.md b/README.md index dfe29de..5114d51 100644 --- a/README.md +++ b/README.md @@ -65,3 +65,19 @@ mkvirtualenv djmusic && pip install -r requirements.txt && add2virtualenv `pwd`/ 4. Create a new template and view function: `dynamic.html`. 5. Add a `
` to this template using [django forms](https://docs.djangoproject.com/en/1.11/topics/forms/) 6. Don't forget about `csrf`! + +## 3. `web_db` + +Now we're going to play with Django's ORM and admin: One of the main selling-points of Django vs other Web Libraries. + +1. Repeat steps 1-8 in `web_pages` +2. Let's create a few DB models in `models.py` +3. Now we need migrations for them + 1. We create the migrations with `./manage.py makemigrations web_db` + 2. And migrate the db with `./manage.py migrate`. Note, this will migrate all apps if you haven't done so already. + 3. Note, if you're using the repo, that we added a `0002` migration to populate with initial data +4. We make things easier by using `include` for our URLS and adding our own urls.py definitions in the app, instead of the site. +5. We also use [Generic Views](https://docs.djangoproject.com/en/1.10/topics/class-based-views/generic-display/) for listing. +6. And we also add an `admin.py` file for easy management of the DB. +7. We can visit `localhost:8000/admin` and `localhost:8000/db/songs`; etc for more views. +8. Because our urls are named, we can use the `{% url %}` django template tag to directly link to a model's page (or the list page). diff --git a/apps/django_music/settings.py b/apps/django_music/settings.py index 974a3f1..c975701 100644 --- a/apps/django_music/settings.py +++ b/apps/django_music/settings.py @@ -41,6 +41,7 @@ OUR_APPS = [ 'web_pages', 'web_app', + 'web_db' ] INSTALLED_APPS = DJANGO_APPS + OUR_APPS diff --git a/apps/django_music/urls.py b/apps/django_music/urls.py index f3004f7..9054f8e 100644 --- a/apps/django_music/urls.py +++ b/apps/django_music/urls.py @@ -13,7 +13,7 @@ 1. Import the include() function: from django.conf.urls import url, include 2. Add a URL to urlpatterns: url(r'^blog/', include('blog.urls')) """ -from django.conf.urls import url +from django.conf.urls import url, include from django.contrib import admin from web_pages.views import basic_web_page @@ -25,4 +25,6 @@ # the web_app app. url(r'^app/$', basic_web_app), url(r'^app/song/$', dynamic_web_app), + # the web_db app. In this case, we'll include instead of specifying each view + url(r'^db/', include('web_db.urls', namespace='db')), ] diff --git a/apps/web_db/__init__.py b/apps/web_db/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/apps/web_db/admin.py b/apps/web_db/admin.py new file mode 100644 index 0000000..db604d0 --- /dev/null +++ b/apps/web_db/admin.py @@ -0,0 +1,19 @@ +from django.contrib import admin + +from web_db import models + + +class SongAdmin(admin.ModelAdmin): + search_fields = ('title',) + + +class SongInline(admin.TabularInline): + model = models.Song + + +class AlbumAdmin(admin.ModelAdmin): + search_fields = ('title', 'artist', 'year',) + inlines = (SongInline,) + +admin.site.register(models.Album, AlbumAdmin) +admin.site.register(models.Song, SongAdmin) diff --git a/apps/web_db/apps.py b/apps/web_db/apps.py new file mode 100644 index 0000000..7b33091 --- /dev/null +++ b/apps/web_db/apps.py @@ -0,0 +1,5 @@ +from django.apps import AppConfig + + +class WebDbConfig(AppConfig): + name = 'web_db' diff --git a/apps/web_db/migrations/0001_initial.py b/apps/web_db/migrations/0001_initial.py new file mode 100644 index 0000000..0b1333a --- /dev/null +++ b/apps/web_db/migrations/0001_initial.py @@ -0,0 +1,36 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.4 on 2017-09-03 00:16 +from __future__ import unicode_literals + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ] + + operations = [ + migrations.CreateModel( + name='Album', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('title', models.CharField(max_length=255)), + ('year', models.PositiveIntegerField()), + ('artist', models.CharField(max_length=255)), + ], + ), + migrations.CreateModel( + name='Song', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('title', models.CharField(max_length=255)), + ('track', models.PositiveIntegerField(blank=True)), + ('duration', models.CharField(blank=True, max_length=15)), + ('album', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='web_db.Album')), + ], + ), + ] diff --git a/apps/web_db/migrations/0002_auto_20170903_0052.py b/apps/web_db/migrations/0002_auto_20170903_0052.py new file mode 100644 index 0000000..910af8c --- /dev/null +++ b/apps/web_db/migrations/0002_auto_20170903_0052.py @@ -0,0 +1,84 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.4 on 2017-09-03 00:52 +from __future__ import unicode_literals +from collections import namedtuple + +from django.db import migrations + + +MUSIC = [ + { + 'artist': 'Les Gordon', + 'title': 'Abyss', + 'year': '2016', + 'songs': [ + { + 'track': 1, + 'title': 'Abyss', + 'duration': '3:13' + }, + { + 'track': 2, + 'title': 'Shiho & Kyoko', + 'duration': '2:58' + }, + ] + }, + { + 'artist': 'Same Gellaitry', + 'title': 'Escapism II', + 'year': '2016', + 'songs': [ + { + 'track': 1, + 'title': 'The Gateway', + 'duration': '3:12' + }, + { + 'track': 2, + 'title': 'Desert Mirage', + 'duration': '5:00' + }, + { + 'track': 3, + 'title': 'Jacket Weather', + 'duration': None + }, + { + 'track': 4, + 'title': 'Static Sleep', + 'duration': '' + } + ] + } + ] + + +class Migration(migrations.Migration): + + dependencies = [ + ('web_db', '0001_initial'), + ] + + def migrate_music(apps, schema_editor): + Album = apps.get_model("web_db", "Album") + Song = apps.get_model("web_db", "Song") + for m in MUSIC: + album = namedtuple('album', m.keys())(*m.values()) + new_album = Album.objects.create( + title=album.title, + year=album.year, + artist=album.artist + ) + for s in album.songs: + song = namedtuple('song', s.keys())(*s.values()) + Song.objects.create( + title=song.title, + duration=song.duration or '', + track=song.track, + album=new_album + ) + + operations = [ + migrations.RunPython(migrate_music, migrations.RunPython.noop), + ] diff --git a/apps/web_db/migrations/__init__.py b/apps/web_db/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/apps/web_db/models.py b/apps/web_db/models.py new file mode 100644 index 0000000..b1633e3 --- /dev/null +++ b/apps/web_db/models.py @@ -0,0 +1,21 @@ +from django.db import models + + +class Album(models.Model): + title = models.CharField(max_length=255) + year = models.PositiveIntegerField() + artist = models.CharField(max_length=255) + + def __str__(self): + return "{} by {} ({})".format(self.title, self.artist, self.year) + + +class Song(models.Model): + title = models.CharField(max_length=255) + track = models.PositiveIntegerField(blank=True) + duration = models.CharField(max_length=15, blank=True) + + album = models.ForeignKey(Album) + + def __str__(self): + return "{} ({})".format(self.title, self.track) diff --git a/apps/web_db/templates/web_db/album_detail.html b/apps/web_db/templates/web_db/album_detail.html new file mode 100644 index 0000000..59cf43f --- /dev/null +++ b/apps/web_db/templates/web_db/album_detail.html @@ -0,0 +1,19 @@ + + + + + Album {{ album.title }} + + +

{{ album.title }} by {{ album.artist }} ({{ album.year }})

+

Songs:

+ + ← back to album list + + diff --git a/apps/web_db/templates/web_db/album_list.html b/apps/web_db/templates/web_db/album_list.html new file mode 100644 index 0000000..987a8fa --- /dev/null +++ b/apps/web_db/templates/web_db/album_list.html @@ -0,0 +1,25 @@ + + + + + Albums + + +

All Albums

+ {% for album in albums %} +

+ + {{ album.title }} by {{ album.artist }} ({{ album.year }}) + +

+

Songs:

+ + {% endfor %} + + diff --git a/apps/web_db/templates/web_db/song_detail.html b/apps/web_db/templates/web_db/song_detail.html new file mode 100644 index 0000000..9cae86b --- /dev/null +++ b/apps/web_db/templates/web_db/song_detail.html @@ -0,0 +1,12 @@ + + + + + Song {{ song.title }} + + +

{{ song.track }}. {{ song.title }} {% if song.duration %}({{ song.duration }}){% endif %}

+

album: {{ song.album.title }}

+ ← back to song list + + diff --git a/apps/web_db/templates/web_db/song_list.html b/apps/web_db/templates/web_db/song_list.html new file mode 100644 index 0000000..8534ac4 --- /dev/null +++ b/apps/web_db/templates/web_db/song_list.html @@ -0,0 +1,18 @@ + + + + + Song {{ song.title }} + + +

All Songs

+ + + diff --git a/apps/web_db/tests.py b/apps/web_db/tests.py new file mode 100644 index 0000000..7ce503c --- /dev/null +++ b/apps/web_db/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/apps/web_db/urls.py b/apps/web_db/urls.py new file mode 100644 index 0000000..bb36318 --- /dev/null +++ b/apps/web_db/urls.py @@ -0,0 +1,10 @@ +from django.conf.urls import url + +from web_db import views + +urlpatterns = [ + url(r'^albums/$', views.AlbumListView.as_view(), name='album-list'), + url(r'^albums/(?P\d+)/$', views.AlbumDetailView.as_view(), name='album-detail'), + url(r'^songs/$', views.SongListView.as_view(), name='song-list'), + url(r'^songs/(?P\d+)/$', views.SongDetailView.as_view(), name='song-detail'), +] diff --git a/apps/web_db/views.py b/apps/web_db/views.py new file mode 100644 index 0000000..4c4752c --- /dev/null +++ b/apps/web_db/views.py @@ -0,0 +1,24 @@ +from django.views.generic.detail import DetailView +from django.views.generic.list import ListView + +from web_db.models import Album, Song + + +class AlbumDetailView(DetailView): + model = Album + context_object_name = 'album' + + +class AlbumListView(ListView): + model = Album + context_object_name = 'albums' + + +class SongDetailView(DetailView): + model = Song + context_object_name = 'song' + + +class SongListView(ListView): + model = Song + context_object_name = 'songs'