Created
September 25, 2014 14:52
-
-
Save sivaa/4e281341c40017360301 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#### Step 0: Installtion/Setup #### | |
Microsoft Windows 7 : http://bit.ly/pycon-gswd-windows-setup | |
GNU/Linux Ubuntu : http://bit.ly/pycon-gswd-linux-setup | |
# Once the above setup is done, pull the latest content from the repository | |
git pull | |
#### Step 1: Django Project Creation #### | |
# [OPTIONAL] Checkout the base branch for this rampup excercise | |
git checkout rampup_base | |
# Create a project using the following command | |
# Windows : | |
python C:\Users\<User name>\Envs\pycon-dj-workshop\Scripts\django-admin.py startproject fav | |
# Linux | |
python ~/.virtualenvs/pycon-dj-workshop/bin/django-admin.py startproject fav | |
# List the created new files | |
# Windows: | |
tree /F | |
# Linux: | |
tree | |
# Navigate to the project directory | |
cd fav | |
# Refer the settings (INSTALLED_APPS and DATABASES) | |
# Create the necessary tables for the INSTALLED_APPS | |
python manage.py syncdb | |
# Choose 'No' for super user creation | |
# Run the development server | |
python manage.py runserver | |
# Verify it in the browser (http://localhost:8000) | |
# Create a demo app | |
python manage.py startapp demo | |
# List the created new files using tree command | |
#### Step 2: Simple Hello World #### | |
# Open fav/demo/views.py and create a view for returning "Hello World!" as a HTTP response | |
from django.http import HttpResponse | |
def hello1(request): | |
return HttpResponse("Hello World!") | |
# Open fav/fav/urls.py and create a routing for the above view | |
url(r'^hello1/', 'demo.views.hello1'), | |
# Make sure that the dev server is running and access the following URL in the browser | |
http://localhost:8000/hello1/ | |
# Write a another view to return the HTML string as a HTTP response | |
def hello2(request): | |
return HttpResponse("<h1>Hello World!</h1>") | |
# Create a routing for the above view in urls.py | |
url(r'^hello2/', 'demo.views.hello2'), | |
# Access the following URL in the browser | |
http://localhost:8000/hello2/ | |
# Write a another view to return the currnet timestamp | |
from datetime import datetime | |
def hello3(request): | |
return HttpResponse("<h1>Hello World! at {} </h1>".format(datetime.now())) | |
# Create a routing for the above view in urls.py | |
url(r'^hello3/', 'demo.views.hello3'), | |
# Access the following URL in the browser | |
http://localhost:8000/hello3/ | |
# Write a another view to fetch and retrun the query string | |
def hello4(request): | |
name = request.GET.get("name", "Django") | |
return HttpResponse("Hello <strong>{}</strong>!".format(name))) | |
# Create a routing for the above view in urls.py | |
url(r'^hello4/', 'demo.views.hello4'), | |
# Access the following URLs in the browser | |
http://localhost:8000/hello4/ | |
http://localhost:8000/hello4/?name=Siva | |
#### Step 3: Hello World with Templates #### | |
# Create a 'templates' directory under fav/demo/ folder | |
mkdir demo/templates | |
# Create a HTML document called 'hello5.html' with the following content | |
<!DOCTYPE html> | |
<html> | |
<head> | |
<title>Introduction to Django Workshop</title> | |
</head> | |
<body> | |
Hello World! | |
</body> | |
</html> | |
# Write a another view to return the above template as a HTTP response | |
from django.shortcuts import render | |
def hello5(request): | |
return render(request, "hello5.html") | |
# Create a routing for the above view in urls.py | |
url(r'^hello5/', 'demo.views.hello5'), | |
# Access the following URL in the browser | |
http://localhost:8000/hello5/ | |
# It will throw the TemplateDoesNotExist exception since this app is not registred. | |
# Registred this app in the INSTALLED_APPS tuple in fav/fav/settings.py | |
'demo', | |
# Try the same url again in the browser | |
# Copy the 'hello5.html and create hello6.html' and update the body with current_time template variable | |
<body> | |
Hello World! at {{ current_time }} | |
</body> | |
# Write a another view to return the currnet timestamp | |
def hello6(request): | |
return render(request, | |
"hello6.html", | |
{'current_time' : datetime.now() }) | |
# Create a routing for the above view in urls.py | |
url(r'^hello6/', 'demo.views.hello6'), | |
# Access the following URL in the browser | |
http://localhost:8000/hello6/ | |
# Copy the 'hello5.html and create hello7.html' and update the body with name template variable | |
<body> | |
Hello <strong> {{ name }} <strong>! | |
</body> | |
# Write a another view to fetch and retrun the query string | |
def hello7(request): | |
name = request.GET.get("name", "Django") | |
return render(request, | |
"hello7.html", | |
{'name' : name }) | |
# Create a routing for the above view in urls.py | |
url(r'^hello7/', 'demo.views.hello7'), | |
# Access the following URLs in the browser | |
http://localhost:8000/hello7/ | |
http://localhost:8000/hello7/?name=Siva | |
# [OPTIONAL] Checkout the complete branch for this rampup excercise to for reference | |
git checkout rampup_complete | |
#### Step 0: Movie app Creation & Registration #### | |
# Create a demo app | |
python manage.py startapp movie | |
# Create a 'templates' directory under fav/movie/ folder | |
mkdir movie/templates | |
# Registred this app in the INSTALLED_APPS tuple in fav/fav/settings.py | |
'movie', | |
#### Step 1: Create Movie Add HTML Form #### | |
# Create fav/movie/templates/movies.html file with the following content | |
<!DOCTYPE html> | |
<html> | |
<head> | |
<title>My Fav Movie List</title> | |
</head> | |
<body> | |
<form action="/movies/" method="post"> | |
Movie Name : <input type="text" name="movie_name"> | |
<input type="submit" value="Add"> | |
</form> | |
</body> | |
</html> | |
# Serve this template in the fav/movie/views.py | |
def movies(request): | |
return render(request, "movies.html") | |
# Open fav/fav/urls.py and create a routing for the above view | |
url(r'^movies/$', 'movie.views.movies'), | |
# Access the following URLs in the browser | |
http://localhost:8000/movies/ | |
# Click "Add" button | |
# Fix the CSRF verification by providing the token inside the form | |
<form action="/movies/" method="post"> {% csrf_token %} | |
#### Step 2: Implement Add functionality and Store it in Database #### | |
# We are using SQLite database and the settings.py wil have the following settings by default | |
DATABASES = { | |
'default': { | |
'ENGINE': 'django.db.backends.sqlite3', | |
'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), | |
} | |
} | |
# Create a model (database table) to store the movie name in fav/movie/models.py | |
class Movie(models.Model): | |
name = models.CharField(max_length = 100, unique = True) | |
# Create the above table using syncdb | |
python manage.py syncdb | |
# It will not create the tables since from 1.7 database/table migration is enabled by default | |
# Perform the migration using the following steps | |
python manage.py makemigrations | |
python manage.py migrate | |
# [OPTIONAL] Verify the table structure in SQLite browser | |
# Access this table in the Django shell and try the following options | |
# Open the shell | |
python manage.py shell | |
# Import the Movie model | |
from movie.models import Movie | |
# Count the number of available records | |
Movie.objects.count() | |
# Create the first record | |
Movie.objects.create(name = "Troy") | |
# Create few more records | |
Movie.objects.create(name = "Terminator") | |
Movie.objects.create(name = "Avatar") | |
Movie.objects.create(name = "Ice Age") | |
# List all the records | |
Movie.objects.all() | |
# Fix the object representation in models.py | |
class Movie(models.Model): | |
name = models.CharField(max_length = 100, unique = True) | |
def __unicode__(self): | |
return self.name | |
# Changes won't be auto reflected. Quit the shell (Ctrl + Z) and reopen it | |
# Import the model and list all the records | |
from movie.models import Movie | |
Movie.objects.all() | |
# Fetch the first record | |
Movie.objects.first() | |
# Fetch the last record | |
Movie.objects.last() | |
# Count the number of available records | |
Movie.objects.count() | |
# Fetch a specific record using get API | |
troy_movie = Movie.objects.get(name="Troy") | |
# Access its fields | |
troy_movie.name | |
troy_movie.id | |
troy_movie.pk | |
# Fetch list of matching records using filter API | |
Movie.objects.filter(name__contains = "T") | |
# Exclude a specific record | |
Movie.objects.exclude(name__contains = "T") | |
Movie.objects.exclude(name__contains = "t") | |
# Chain multiple APIs | |
Movie.objects.filter(name__contains = "a").exclude(name__contains = " ") | |
# Delete a specific record | |
troy_movie = Movie.objects.get(name="Troy") | |
troy_movie.delete() | |
# Delete all records | |
Movie.objects.all().delete() | |
# Explore all the others APIs from this link https://docs.djangoproject.com/en/dev/ref/models/querysets/ | |
# Handle POST request, extract the movie_name and persist in database | |
from django.http import HttpResponse | |
from movie.models import Movie | |
def movies(request): | |
if request.method == 'GET': | |
return render(request, "movies.html") | |
if request.method == 'POST': | |
movie_name = request.POST.get("movie_name") | |
Movie.objects.create(name = movie_name) | |
message = "Movie '{}' is added successfully.".format(movie_name) | |
return HttpResponse(message) | |
return ("Invalid Request") | |
# Test this feature by adding a movie name in the browser. | |
#### Step 3: Implement the notification message in the same page #### | |
# Add the message variable in the template | |
<body> | |
{% if message %} | |
{{ message }} <br> <br> | |
{% endif %} | |
# <HTML From> | |
</body> | |
# Pass the message variable to template | |
if request.method == 'POST': | |
# Other lines | |
return render(request, | |
"movies.html", | |
{"message" : message}) | |
#### Step 4: List all the available movies before Add form #### | |
# Display the movies list in the HTML | |
<body> | |
# <Notification Message> | |
{% if movies %} | |
{% for movie in movies %} | |
<li> {{ movie.name }} </li> | |
{% endfor %} | |
{% else %} | |
No movies added so far. <br> | |
{% endif %} <br> | |
# <HTML Form> | |
</body> | |
# Pass the movies list from views to template | |
def _get_movies(): | |
return Movie.objects.all() | |
def movies(request): | |
if request.method == 'GET': | |
return render(request, | |
"movies.html", | |
{"movies" : _get_movies()}) | |
if request.method == 'POST': | |
# Other lines | |
return render(request, | |
"movies.html", | |
{"message" : message, | |
"movies" : _get_movies()}) | |
#### Step 5: Implement Remove feature in HTTP GET #### | |
# Add the remove link before each movie name | |
{% for movie in movies %} | |
<li> <a href="/movie/remove/?id={{ movie.pk }}"> [x] </a> {{ movie.name }} </li> | |
{% endfor %} | |
# Implement the routing for remove option in urls.py | |
url(r'^movie/remove/$', 'movie.views.remove_movie'), | |
# Remove the selected movie name from the persistence | |
def remove_movie(request): | |
if request.method == 'GET': | |
movie_id = request.GET.get("id") | |
movie = Movie.objects.get(id = movie_id) | |
movie.delete() | |
message = "Movie '{}' is removed successfully.".format(movie.name) | |
return render(request, | |
"movies.html", | |
{"message" : message, | |
"movies" : _get_movies()}) | |
return ("Invalid Request") | |
#### Step 6: Validations, Integrity and other Error #### | |
# Implement movie length validation and handle IntegrityError | |
from django.db.utils import IntegrityError | |
def movies(request): | |
# GET implementation | |
if request.method == 'POST': | |
movie_name = request.POST.get("movie_name") | |
movie_name = movie_name.strip() | |
if not movie_name: | |
message = "Enter a Movie Name" | |
elif len(movie_name) < 3: | |
message = "Not enough words!" | |
else: | |
try: | |
Movie.objects.create(name = movie_name) | |
message = "Movie '{}' is added successfully.".format(movie_name) | |
except IntegrityError: | |
message = "Movie '{}' is already exists.".format(movie_name) | |
# Remaining code here | |
# Handle the invalid movie id in the remove method | |
try: | |
movie = Movie.objects.get(id = movie_id) | |
movie.delete() | |
message = "Movie '{}' is removed successfully.".format(movie.name) | |
except Movie.DoesNotExist as e: | |
message = "Given movie does not exists." | |
#### Step 7: Implement Django Forms and remove HTML Form #### | |
# Create fav/movie/forms.py and implement Django Form | |
from django import forms | |
class MovieForm(forms.Form): | |
movie_name = forms.CharField(required = False) | |
# Enhance the 'movies' view to use the above form | |
def movies(request): | |
if request.method == 'GET': | |
return render(request, | |
"movies.html", | |
{"movies" : _get_movies(), | |
"form" : MovieForm()}) # Added | |
if request.method == 'POST': | |
form = MovieForm(request.POST) | |
if form.is_valid(): | |
movie_name = form.cleaned_data["movie_name"] | |
# Remaining code | |
try: | |
Movie.objects.create(name = movie_name) | |
message = "Movie '{}' is added successfully.".format(movie_name) | |
form = MovieForm() # Added | |
except IntegrityError: | |
message = "Movie '{}' is already exists.".format(movie_name) | |
return render(request, | |
"movies.html", | |
{"message" : message, | |
"movies" : _get_movies(), | |
"form" : form}) # Added | |
# Enhance the 'remove_movie' view to use the django form | |
def remove_movie(request): | |
# Other code | |
return render(request, | |
"movies.html", | |
{"message" : message, | |
"movies" : _get_movies(), | |
"form" : MovieForm()}) # Added | |
# Enhance the template to use the django form | |
<form action="/movies/" method="post"> {% csrf_token %} | |
{{ form }} | |
<input type="submit" value="Add"> | |
</form> | |
#### Step 8: Move Validations from views to Django Form #### | |
# Implement the validations in the Django form | |
class MovieForm(forms.Form): | |
movie_name = forms.CharField(required = True) | |
def clean_movie_name(self): | |
movie_name = self.cleaned_data['movie_name'].strip() | |
if len(movie_name) < 3: | |
raise forms.ValidationError("Not enough words!") | |
return movie_name | |
# Remove the validations from views | |
if request.method == 'POST': | |
form = MovieForm(request.POST) | |
if form.is_valid(): | |
movie_name = form.cleaned_data["movie_name"].strip() | |
try: | |
# Other code | |
except IntegrityError: | |
message = "Movie '{}' is already exists.".format(movie_name) | |
else: | |
message = "Please correct all the validation errors below." | |
#### Step 9: Implement Delete Confirm using GET and delete it in POST #### | |
## Lets use URL string instead of query string for remove | |
# Enhance the HTML | |
<li> <a href="/movie/remove/{{ movie.pk }}/"> [x] </a> {{ movie.name }} </li> | |
# Enhance the routing configuration (urls.py) to accomodate the same | |
url(r'^movie/remove/(?P<movie_id>\d+)/$', 'movie.views.remove_movie'), | |
# Create a new template for delete confirmation fav/movie/templates/movie_delete_confirm.html | |
<!DOCTYPE html> | |
<html> | |
<head> | |
<title>My Fav Movie List - Delete Confirmation</title> | |
</head> | |
<body> | |
<form method="post"> {% csrf_token %} | |
Would you like to delete the movie <b> '{{ movie.name }}' </b>? | |
<input type="submit" value="Yep. Sure!"> | |
</form> | |
</body> | |
</html> | |
# Enhance the remove view for confirmation and delete using post | |
def remove_movie(request, movie_id): | |
if request.method == 'GET': | |
try: | |
movie = Movie.objects.get(id = movie_id) | |
return render(request, | |
"movie_delete_confirm.html", | |
{ 'movie' : movie}) | |
except Movie.DoesNotExist as e: | |
message = "Given movie does not exists." | |
return render(request, | |
"movies.html", | |
{"message" : message, | |
"movies" : _get_movies(), | |
"form" : MovieForm()}) | |
if request.method == 'POST': | |
try: | |
movie = Movie.objects.get(id = movie_id) | |
movie.delete() | |
message = "Movie '{}' is removed successfully.".format(movie.name) | |
except Movie.DoesNotExist as e: | |
message = "Given movie does not exists." | |
return render(request, | |
"movies.html", | |
{"message" : message, | |
"movies" : _get_movies(), | |
"form" : MovieForm()}) | |
return ("Invalid Request") | |
#### Step 10: Implement Edit feature #### | |
# Add the Edit option the HTML (movies.html) | |
<li> <a href="/movie/remove/{{ movie.pk }}/"> [x] </a> | |
<a href="/movie/edit/{{ movie.pk }}/"> [Edit] </a> | |
{{ movie.name }} | |
</li> | |
# Add the routing for edit feature | |
url(r'^movie/edit/(?P<movie_id>\d+)/$', 'movie.views.edit_movie'), | |
# Create a new template for edit fav/movie/templates/edit.html | |
<!DOCTYPE html> | |
<html> | |
<head> | |
<title>My Fav Movie List - Edit</title> | |
</head> | |
<body> | |
{% if message %} | |
{{ message }} <br> <br> | |
{% endif %} | |
<form method="post"> {% csrf_token %} | |
{{ form }} | |
<input type="submit" value="Edit"> | |
</form> | |
</body> | |
</html> | |
# Enhance the remove view for confirmation and delete using post | |
def edit_movie(request, movie_id): | |
if request.method == 'GET': | |
try: | |
movie = Movie.objects.get(id = movie_id) | |
form = MovieForm(initial = {'movie_name': movie.name }) | |
return render(request, | |
"movie_edit.html", | |
{ 'form' : form}) | |
except Movie.DoesNotExist as e: | |
message = "Given movie does not exists." | |
return render(request, | |
"movies.html", | |
{"message" : message, | |
"movies" : _get_movies(), | |
"form" : MovieForm()}) | |
if request.method == 'POST': | |
form = MovieForm(request.POST) | |
if form.is_valid(): | |
movie_name = form.cleaned_data["movie_name"].strip() | |
try: | |
movie = Movie.objects.get(id = movie_id) | |
movie.name = movie_name | |
movie.save() | |
message = "Movie '{}' is successfully.".format(movie_name) | |
form = MovieForm() | |
except IntegrityError: | |
message = "Movie '{}' is already exists.".format(movie_name) | |
except Movie.DoesNotExist as e: | |
message = "Given movie does not exists." | |
else: | |
message = "Please correct all the validation errors below." | |
return render(request, | |
"movie_edit.html", | |
{ 'form' : form}) | |
return render(request, | |
"movies.html", | |
{"message" : message, | |
"movies" : _get_movies(), | |
"form" : form}) | |
return ("Invalid Request") | |
#### Step 11: Enable Django Admin for Movie Model #### | |
# Enhance the Django Form to use Django Model Form | |
from movie.models import Movie | |
class MovieForm(forms.ModelForm): | |
# movie_name = forms.CharField(required = True) | |
class Meta: | |
model = Movie | |
def clean_name(self): | |
movie_name = self.cleaned_data['name'].strip() | |
if len(movie_name) < 3: | |
raise forms.ValidationError("Not enough words!") | |
return movie_name | |
# Create a Admin class for Movie Model and register it (fav/movie/admin.py) | |
from movie.models import Movie | |
from movie.forms import MovieForm | |
class MovieAdmin(admin.ModelAdmin): | |
form = MovieForm | |
admin.site.register(Movie, MovieAdmin) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment