The document discusses the benefits of using the Model-View-Controller (MVC) architecture for web applications. It introduces Giotto, a new Python framework that enforces MVC patterns. Giotto defines application features that map to a controller, model, and view. Features can be accessed via HTTP, command line, or other interfaces. This ensures separation of concerns and clear assignment of responsibilities in the code. The document provides examples of how Giotto implements common MVC patterns like model views and controllers.
2. Table on contents
First part: Why high level code organization schemes are
important
Second part: All about MVC
Third Part: Giotto!
3. My experiences
1. Started web development in 2007
2. Wrote code without any kind of architectural pattern at all
3. This was very frustrating, but I didn't know any better
4. Realized it's taking too long to get stuff fixed and its not fun
5. Learned first hand that not using MVC is a pain
5. Flightlogg.in'
Was originally PHP (non-MVC)
Now is django (mostly MVC)
View: HTML/Javascript
Controller: Standard HTTP GET/POST
Model: Flight Storage and flight data analysis.
12. Overview
1. Models - The application
2. Controllers - The interface
a. ties your application (model) to the outside world
3. Views - The presentation of the output to the user
13. Models
1. Usually the biggest part of your application
2. Business Logic
3. Not just database tables
4. Should be completely controller independent
14. Views
1. All about formatting output from model.
2. Templates/HTML
3. Serializers
4. Should be independent of any controllers (templates are
portable)
15. Controllers
1. How the data gets to and from the user
2. Apache, nginx, varnish, django middleware, mod_wsgi are
all technically part of the controller.
3. What goes into my controller?
a. High level Model code
b. High level View code
c. final interface level operations
19. Don't put non-controller code inside a
controller!
def to_decimal(input):
"""
>>> to_decimal('3:30')
3.5
>>> to_decimal('3.5')
3.5
>>> to_decimal('3:12')
3.2
"""
This is not a controller function! Not high level model code, not
high level view code, and not interface specific!!!
20. This code is not controller code!
def controller(request):
total_time = to_decimal(request.GET['total_time']) # bad!
landings = request.GET['landings']
user = request.user
flight = Flight.new_flight(user, total_time, landings)
try:
flight.save()
except:
raise HttpResponseError("invalid flight data")
return render_to_response(
context={'flights': Flight.objects.filter(request.user)}
template='flights.html')
21. The three line MVC application!
def mini_controller(request):
return {total_time: request.GET['total_time'],
landings: request.GET['landings'],
user: request.user}
def new_flight(request):
args = mini_controller(request)
flight = Flight.new_flight(*args).save()
return render_to_response('view_flight.html', {'flight': flight})
22. The MVC color-wheel
Model
Middleware
ModelViews /
Forms
Controller View
Context processors
23. ModelViews
1. Projection of a Model (subclass) intended for use in a set of
views
2. Atomic elements that should not hinder the 'real' view's ability
to do its job.
26. ModelView
flights.html:
<table class="whatever">
{{ Flight.header }}
{% for flight in flights %}
{{ flight.as_tr }}
{% endfor %}
</table>
flights.json:
{user: {{ request.user }},
flights:
{% for flight in flights %}
{{ flight.as_json }},
{% endfor %}
}
27. Good models are easy to test
class BaseFlightFailedTest(object):
exc = Flight.InvalidFlightData
def test(self):
for kwargs in self.kwarg_set:
self.assertRaises(Flight.new_flight(**kwargs), self.exc)
class TotalGreatestTest(TestCase, BaseFlightFailedTest):
exc = Flight.TotalMustBeGreatest
kwarg_set = [{'total_time': '3:50', 'pic': 9.6},
{'total_time': 1.0, 'night': 2.3}]
class NoNightTime(TestCase, BaseFlightFailedTest)
kwarg_set = [{'total_time': 1, 'night': 0, 'night_landings': 5}]
28. Tips:
1. Don't pass request objects into the model
a. It couples your model to HTTP requests
b. Models should only work with raw data
2. Try to avoid putting business logic into a controller
a. It makes it hard to reuse models
3. Pass in only one model object to a view.
a. if you have trouble doing this, your models may be wrong
b. helps keep the templates reusable.
4. Make an attempt to re-write all your controllers to be
exactly 3 lines.
29. Giotto!
- New python web development framework!!
- Absolutely nothing has been started yet.
- Doesn't let you violate MVC.
- There should be one-- and preferably only one --obvious way
to do it.
- "MV" framework. (micro controllers)
- Completely automatic urls
- plugins for features
- plugins for controller backends. (commandline, http-get, etc)
30. Giotto Feature
@interfaces('http-get', 'commandline')
class ShowFlightsForUser(Feature):
"""
Show flights for a given user
"""
controller = {'user': Input.data.user}
model = Flight.objects.show_for_user
view = ShowFlights
url for feature:
{% http-get ShowFlightsForUser.html 59 %} ->
Logbook.com/flights/ShowFlightsForUser.html?user=59
31. Giotto Interfaces
@interfaces('http-put', 'commandline')
class NewFlight(Feature):
"""
Create a new flight
"""
controller = {'user': Input.data.user, 'total_time': Input.data...
model = Logbook.Flight.create
view = SingleFlight
Using the feature:
$ ./giotto.py logbook NewFlight --user=chris --total_time=3 ...
or
PUT /new_flight.json HTTP/1.1
user=chris&total_time=3 ...
32. Giotto Models
class Manager(models.Manager)
def show_for_user(self, user):
return self.filter(user=user)
def create(self, *args, **kwargs):
# logic goes here
return Flight(**kwargs)
class Flight(models.Model):
attribute = models.Field()
objects = Manager()
33. Accessing features
command line:
$ ./giotto.py app feature format [args]
http-get
POST app.com/feature.format HTTP/1.1
[args]
sms
text "feature format [args]" to 3558526
The controller backend handles transporting data to/from the
user
34. Controllers handle everything for you
@interfaces('http-get', 'commandline')
class ShowRouteForFlight(Feature):
"""
Get the route for a single flight
"""
controller = Flight.id
model = Flight.route
view = SingleRouteView
35. Giotto Views
Take only a single model instance as the only context (obj)
Views can return anything, as long as the controller backend
knows how to handle it.
templates make links to application features:
<a href="{% url_get ShowFlightsForUser obj.user %}">
see {{ obj.user }}'s flights!
</a>