Download - Models, controllers and views
![Page 1: Models, controllers and views](https://reader036.vdocuments.site/reader036/viewer/2022062405/555673c4d8b42a144c8b52a9/html5/thumbnails/1.jpg)
The Three line MVC application
and introducing Giotto
![Page 2: Models, controllers and views](https://reader036.vdocuments.site/reader036/viewer/2022062405/555673c4d8b42a144c8b52a9/html5/thumbnails/2.jpg)
Table on contents
First part: Why high level code organization schemes are important
Second part: All about MVC
Third Part: Giotto!
![Page 3: Models, controllers and views](https://reader036.vdocuments.site/reader036/viewer/2022062405/555673c4d8b42a144c8b52a9/html5/thumbnails/3.jpg)
My experiences
1. Started web development in 20072. Wrote code without any kind of architectural pattern at all3. This was very frustrating, but I didn't know any better4. Realized it's taking too long to get stuff fixed and its not fun5. Learned first hand that not using MVC is a pain
![Page 4: Models, controllers and views](https://reader036.vdocuments.site/reader036/viewer/2022062405/555673c4d8b42a144c8b52a9/html5/thumbnails/4.jpg)
Non-MVC code
![Page 5: Models, controllers and views](https://reader036.vdocuments.site/reader036/viewer/2022062405/555673c4d8b42a144c8b52a9/html5/thumbnails/5.jpg)
Flightlogg.in'
View: HTML/JavascriptController: Standard HTTP GET/POSTModel: Flight Storage and flight data analysis.
Was originally PHP (non-MVC)Now is django (mostly MVC)
![Page 6: Models, controllers and views](https://reader036.vdocuments.site/reader036/viewer/2022062405/555673c4d8b42a144c8b52a9/html5/thumbnails/6.jpg)
Why?
1. Flexibility 2. Organization
![Page 7: Models, controllers and views](https://reader036.vdocuments.site/reader036/viewer/2022062405/555673c4d8b42a144c8b52a9/html5/thumbnails/7.jpg)
Imagine...
1 app1 django view, 9000 lines of code
![Page 8: Models, controllers and views](https://reader036.vdocuments.site/reader036/viewer/2022062405/555673c4d8b42a144c8b52a9/html5/thumbnails/8.jpg)
Imagine...
We want to fix this. Refactor!!
step 1: 1000 functions, 9 lines each
![Page 9: Models, controllers and views](https://reader036.vdocuments.site/reader036/viewer/2022062405/555673c4d8b42a144c8b52a9/html5/thumbnails/9.jpg)
Imagine...
step 2: 100 classes, 10 functions each
![Page 10: Models, controllers and views](https://reader036.vdocuments.site/reader036/viewer/2022062405/555673c4d8b42a144c8b52a9/html5/thumbnails/10.jpg)
And then...
App Models class class Views class class Controllers class class
![Page 11: Models, controllers and views](https://reader036.vdocuments.site/reader036/viewer/2022062405/555673c4d8b42a144c8b52a9/html5/thumbnails/11.jpg)
![Page 12: Models, controllers and views](https://reader036.vdocuments.site/reader036/viewer/2022062405/555673c4d8b42a144c8b52a9/html5/thumbnails/12.jpg)
Overview
1. Models - The application2. Controllers - The interface a. ties your application (model) to the outside world3. Views - The presentation of the output to the user
![Page 13: Models, controllers and views](https://reader036.vdocuments.site/reader036/viewer/2022062405/555673c4d8b42a144c8b52a9/html5/thumbnails/13.jpg)
Models
1. Usually the biggest part of your application2. Business Logic3. Not just database tables4. Should be completely controller independent
![Page 14: Models, controllers and views](https://reader036.vdocuments.site/reader036/viewer/2022062405/555673c4d8b42a144c8b52a9/html5/thumbnails/14.jpg)
Views
1. All about formatting output from model.2. Templates/HTML3. Serializers4. Should be independent of any controllers (templates are portable)
![Page 15: Models, controllers and views](https://reader036.vdocuments.site/reader036/viewer/2022062405/555673c4d8b42a144c8b52a9/html5/thumbnails/15.jpg)
Controllers
1. How the data gets to and from the user2. 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
![Page 16: Models, controllers and views](https://reader036.vdocuments.site/reader036/viewer/2022062405/555673c4d8b42a144c8b52a9/html5/thumbnails/16.jpg)
An example controller
def new_flight_controller(request): total_time = request.POST['total_time'] landings = request.POST['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')
![Page 17: Models, controllers and views](https://reader036.vdocuments.site/reader036/viewer/2022062405/555673c4d8b42a144c8b52a9/html5/thumbnails/17.jpg)
An example controller
def new_flight_controller(request): total_time = request.POST['total_time'] landings = request.POST['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')
Interface operations
High level model code
High level view code
![Page 18: Models, controllers and views](https://reader036.vdocuments.site/reader036/viewer/2022062405/555673c4d8b42a144c8b52a9/html5/thumbnails/18.jpg)
Another example controllerclass NewFlightCommand(BaseCommand): option_list = BaseCommand.option_list + ( make_option('--total_time', '-t', dest='total_time'), make_option('--landings', '-l', dest='landings'), make_option('--user', '-u', dest='user') ) def handle(self, *args, **options): flight = Flight.new_flight(**options) try: flight.save() except: print "Invalid flight data" print "Flight saved!"
![Page 19: Models, controllers and views](https://reader036.vdocuments.site/reader036/viewer/2022062405/555673c4d8b42a144c8b52a9/html5/thumbnails/19.jpg)
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!!!
![Page 20: Models, controllers and views](https://reader036.vdocuments.site/reader036/viewer/2022062405/555673c4d8b42a144c8b52a9/html5/thumbnails/20.jpg)
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')
![Page 21: Models, controllers and views](https://reader036.vdocuments.site/reader036/viewer/2022062405/555673c4d8b42a144c8b52a9/html5/thumbnails/21.jpg)
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})
![Page 22: Models, controllers and views](https://reader036.vdocuments.site/reader036/viewer/2022062405/555673c4d8b42a144c8b52a9/html5/thumbnails/22.jpg)
The MVC color-wheelModel
ViewController
ModelViews / Forms
Context processors
Middleware
![Page 23: Models, controllers and views](https://reader036.vdocuments.site/reader036/viewer/2022062405/555673c4d8b42a144c8b52a9/html5/thumbnails/23.jpg)
ModelViews
1. Projection of a Model (subclass) intended for use in a set of views2. Atomic elements that should not hinder the 'real' view's ability to do its job.
![Page 24: Models, controllers and views](https://reader036.vdocuments.site/reader036/viewer/2022062405/555673c4d8b42a144c8b52a9/html5/thumbnails/24.jpg)
ModelViews
class HTMLFlight(Flight): def as_tr(self): """ >>> HTMLFlight.objects.get(pk=234321).as_tr() '<tr id="flight_234321"><td class="total_time">3.5</td>... """
class JSONFlight(Flight): def as_json(self): """ >>> JSONFlight.objects.get(pk=56216).as_json() '{id: 56216, plane: {tailnumber: "N63NE", type: "SA-227"... """
![Page 25: Models, controllers and views](https://reader036.vdocuments.site/reader036/viewer/2022062405/555673c4d8b42a144c8b52a9/html5/thumbnails/25.jpg)
ModelView
def list_flights_controller(request, format): if format == 'json': return JSONFlight, 'flights.json' elif format == 'html': return HTMLFlight, 'flights.html'
def list_flights(request, format): Flight, view = list_flights_controller(request, format) flights = Flight.objects.filter(user=request.user) return render_to_response({'flights': flights}, view)
![Page 26: Models, controllers and views](https://reader036.vdocuments.site/reader036/viewer/2022062405/555673c4d8b42a144c8b52a9/html5/thumbnails/26.jpg)
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 %}}
![Page 27: Models, controllers and views](https://reader036.vdocuments.site/reader036/viewer/2022062405/555673c4d8b42a144c8b52a9/html5/thumbnails/27.jpg)
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}]
![Page 28: Models, controllers and views](https://reader036.vdocuments.site/reader036/viewer/2022062405/555673c4d8b42a144c8b52a9/html5/thumbnails/28.jpg)
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.
![Page 29: Models, controllers and views](https://reader036.vdocuments.site/reader036/viewer/2022062405/555673c4d8b42a144c8b52a9/html5/thumbnails/29.jpg)
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)
![Page 30: Models, controllers and views](https://reader036.vdocuments.site/reader036/viewer/2022062405/555673c4d8b42a144c8b52a9/html5/thumbnails/30.jpg)
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
![Page 31: Models, controllers and views](https://reader036.vdocuments.site/reader036/viewer/2022062405/555673c4d8b42a144c8b52a9/html5/thumbnails/31.jpg)
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 ...
![Page 32: Models, controllers and views](https://reader036.vdocuments.site/reader036/viewer/2022062405/555673c4d8b42a144c8b52a9/html5/thumbnails/32.jpg)
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()
![Page 33: Models, controllers and views](https://reader036.vdocuments.site/reader036/viewer/2022062405/555673c4d8b42a144c8b52a9/html5/thumbnails/33.jpg)
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
![Page 34: Models, controllers and views](https://reader036.vdocuments.site/reader036/viewer/2022062405/555673c4d8b42a144c8b52a9/html5/thumbnails/34.jpg)
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
![Page 35: Models, controllers and views](https://reader036.vdocuments.site/reader036/viewer/2022062405/555673c4d8b42a144c8b52a9/html5/thumbnails/35.jpg)
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>
![Page 36: Models, controllers and views](https://reader036.vdocuments.site/reader036/viewer/2022062405/555673c4d8b42a144c8b52a9/html5/thumbnails/36.jpg)
Giotto Views
class SingleRouteView(View): def png(self, route): "Given a route, return as a png image" return image_file
def kml(self, route): return kml_string
def html(self, route): return jinja2.render({'obj': route}, 'route.html')
{% http-get RouteForFlight.kml 36426 %}{% http-get RouteForFlight.html 36426 %}giotto logbook RouteForFlight png 36426 | file.png