=== edson is now known as ecanto === kermiac_ is now known as kermiac === nha is now known as Guest14985 === Homer is now known as Guest29756 === kermiac is now known as kermiac_ === leoquant is now known as soonerorlater === soonerorlater is now known as leoquant === andylockran is now known as zrmt === zrmt is now known as andylockran === test is now known as Guest13870 === goat is now known as Guest68340 === Knightlust is now known as Igorots === einand is now known as Real_einand === Real_einand is now known as einand [13:04] * shadeslayer is excited about tomorrow === eXe is now known as Guest23458 === Igorots is now known as Knightlust [14:13] tomorrow? [14:15] cjohnston: eh? [14:15] cjohnston: oh ill be giving a packaging session ;) [14:15] gotcha [14:16] :D [14:16] cjohnston: and hggdh is giving a bug q&a [14:16] I knew about hggdh's [14:16] I knew about the PPA thing too.. [14:16] just fergot about it [14:16] heh [14:17] since I put it on the calendar.. [14:17] lol [14:17] :P [14:17] shadeslayer: PM... [14:17] im just writing notes write now on it [15:18] what happens with me? [15:32] hggdh: you are doing a bug q&a later [15:32] cjohnston: yes, indeed. Adn you wanted to talk with me [15:33] yup.. Got a few? [15:35] yes [15:35] PM hggdh === ShadowChild is now known as lukjad86 === ChanServ changed the topic of #ubuntu-classroom to: Welcome to the Ubuntu Classroom - http://wiki.ubuntu.com/Classroom || Support in #ubuntu || Upcoming Schedule: http://is.gd/8rtIi || Event: Intro to Django - Current Session: Learning Django - Part 3 - Instructor: mhall119|work || Questions in #ubuntu-classroom-chat [17:01] hello and good afternoon [17:01] today we're going to do a double class, since I had to cancel yesterday [17:02] for those of you who misses yesterday, logs from the first two sessions can be found here: https://launchpad.net/classroom-scheduler [17:02] A quick recap of what we covered yesterday... [17:03] we made Django Models for Session (a single online class), Course (a collection of classes), and LernidSession (a derivative of Session, that includes a URL for slides) [17:04] we also configured the Django Admin application so we can view our Models and create instances of them in the database [17:04] today we finally get to the rewarding part of Django, creating the views and forms that will let our users interact with our models [17:05] now, before we begin, I made some changes to the application that are going to require you to modify your database [17:06] If you go to https://wiki.ubuntu.com/mhall119/classes/LearningDjango and scroll down to the section for Day 3 [17:06] you will see my note [17:06] I'll briefly explain what changes I made and why [17:07] "class" is a python keyword for defining, what else, classes [17:07] by calling my application 'class' initially, I caused a conflict with this keyword when I tried to use it as a namescape in python [17:08] so, I renamed the application to "classroom" to avoid the conflict [17:08] however, when django created tables in the database, it defaults to naming them "application_model" [17:08] by changing the name of the application, I also changed the name of the tables it will use [17:09] so, once you run "bzr pull -r tag:day3.1" [17:10] you will need to either re-run "python manage.py syncdb", or just download the classroom_scheduler.db I uploaded here: https://wiki.ubuntu.com/mhall119/classes/LearningDjango?action=AttachFile&do=view&target=classroom_scheduler.db [17:10] I'll give a few minutes for everyone to get their local copy updated to day3.1 [17:11] ask a QUESTION: if you have trouble with this [17:12] if you update your old database, go into the admin and create a session and course for testing [17:13] alright, time to get started [17:13] day3.1 brought in several updates [17:14] it creates a new view method "schedule_view" [17:14] along with templates/schedule_view.html [17:14] and wired them up to the url /schedule/ [17:15] if you again run "python manage.py runserver" you should see something like this: http://www.growingupfree.org:8001/schedule/ [17:16] let's look into classroom/views.py [17:16] as you can see, schedule_view is very simple [17:17] Session.objects.filter() returns a list of Session objects from the database [17:17] you can add key/value pairs to filter() to restrict what is returned, we'll see that in action later [17:17] next it creates a Context instance, and adds the list of sessions to it [17:18] it then calls render_to_response, passing it the name of the template and the context [17:18] render_to_response is a utility method that will render the template into an HttpResponse object for us [17:19] now look at classroom/templates/schedule_view.html [17:19] Django templates are HTML with additional markup [17:20] {% %} blocks are "tags" that perform logic operations [17:20] {{ }} blocks will inline the value of the contained expression into the HTML [17:21] Django supplies many built in tags for you to use, and you can see we are using the {% for value in list %} tag here to iterate through our list of sessions [17:21] you'll also notice that the {{ }} blocks let you properties of a variable as well [17:22] so {{ session.course.title }} will try to get the title of the course this session is a part of [17:22] any questions so far? [17:23] alright, moving on [17:24] run: "bzr pull -r tag:day3.2" [17:24] then "bzr diff -r tag:day3.1" [17:25] here you can see that we are using the {% if %}{% else %}{% endif %} blocks [17:25] then do exactly what you would expect [17:26] {% if value %} will be true if value has a non-false value in python [17:26] so, None, 0 and empty lists will execute the {% else %} block [17:26] {% else %} is optional [17:27] now we are currently returning the datatime as python's default string representation [17:28] but if you look at the schedule on the wiki https://wiki.ubuntu.com/Classroom you see that the date and time are split into 2 columns [17:28] if you run "bzr pull -r tag:day3.3", then "bzr diff -r tag:day3.2" [17:29] you will see that i split it into 2 columns, and use template "filters" to format what is displayed [17:29] again, Django provides a number of built in filters such as "date" [17:30] filters use the syntax: {{variable|filter:"extra"}} [17:30] the :"extra" is optional, not all filters need it [17:31] filters are used to act on the value of a variable, and return a different result [17:31] Now you should be seeing something like this: http://www.growingupfree.org:8001/schedule/ [17:31] we're starting to look a lot more like the wiki schedule now [17:32] hemanth asked: can the filtering of time and date be done as per the clients local time? [17:33] I don't think so, if store your time in UTC, you will have to change it to local time before using the filter [17:33] now, looking back at https://wiki.ubuntu.com/Classroom#Schedule [17:34] you will see that their times are links to an external site that does give times in several locations [17:34] hemanth asked: can't we get the location of the client and do UTC +/- ? [17:35] yes, and in fact python has a helpful method of doing this in datetime.timedelta [17:35] alright, so next we want to turn out time display into a link [17:36] we can do this in our template itself, but this is used all the time within the Ubuntu wiki when times are displayed, and we don't really want to copy/paste it into every template we make [17:36] instead, we're going to make our own template filter to do it [17:36] run "bzr pull -r tag:day3.4" [17:37] and then "bzr diff -r tag:day3.3" [17:37] you will see the new file classroom/templatetags/scheduler_tools.py [17:38] this a file that will contain any number of custom tags and filters [17:38] we use {% load scheduler_tools %} to include them in our schedule_view.html template [17:39] then we can get the URL for our link by simply using {{session.start_time|timelink}} [17:39] if you look at classroom/templatetags/scheduler_tools.py, you see that filters are pretty easy to write [17:40] they are basically a function that takes the associated variable as an argument (and optionally any "extra" values) === edsoncanto is now known as ecanto [17:40] and returns a value to be inlined into the HTML === ecanto is now known as edson === yofel_ is now known as yofel [17:41] @register.filter is a python decorator that registers your filter with Django when {% load %} is called on the file [17:41] now, just as Filters are exceptionally easy to write, Tags are quite hard [17:41] which is why I am not covering them in this series [17:42] but if you are curious, you can view them in the Django source files to see how they are implemented [17:42] any questions at this point? [17:43] anyone need a break before we get started on the next hour? [17:44] lsteeger asked: yes. how about 5 mins? [17:45] okay, we'll take a short break then [17:52] okay, is everyone back? [17:53] alright [17:53] time to get into forms! [17:54] much like a model, a Form is a collection of Field instances that map python values to HTTP parameters [17:55] that makes it easy to pass the contents of request.GET or request.POST to a Form, and get the right python data types out [17:55] form fields will validate their own content, and raise an exception of it can not be converted into the right python data type [17:56] for example, if you pass "abc" to an IntegerField, it will raise an exception [17:57] you call form.is_valid() to determine if all the fields in a form contain well formatted values [17:57] you can then get the corresponding python data type from form.cleaned_data[] [17:58] Django form fields also contain widgets that are used to create the HTML necessary to build the form [17:58] so you don't even have to worry about that [17:59] a full listing of Django's form fields can be seen here: http://docs.djangoproject.com/en/1.1/ref/forms/fields/#ref-forms-fields [17:59] however, you won't actually have to explicitly use them all the time === ChanServ changed the topic of #ubuntu-classroom to: Welcome to the Ubuntu Classroom - http://wiki.ubuntu.com/Classroom || Support in #ubuntu || Upcoming Schedule: http://is.gd/8rtIi || Event: Intro to Django - Current Session: Learning Django - Part 4 - Instructor: mhall119|work || Questions in #ubuntu-classroom-chat [18:01] Django provides a helpful ModelForm type, that will create a Form class with all the fields [18:01] in your Model [18:01] run "bzr pull -r tag:day3.6" [18:02] and "bzr diff -r tag:day3.4" [18:02] (yes, day3.5 is missing) [18:02] now, you will see a new file: classroom/forms.py [18:03] this file contains 2 very small classes, SessionForm and CourseForm, both of which extend Django's ModelForm [18:03] you will also notice that, just like your Models, it contains an internal Meta class [18:03] in this class, we specify which of our Model definitions we want this Form class to use [18:04] and that's really all we need [18:05] we can now call form = SessionForm(request.POST) [18:06] we can also bind an instance of our model to the form: form = SessionForm(instance=mysession, data=request.POST) [18:06] if we have a bound instance, we can call form.save() to apply all of the HTTP values to the instance, and save the instance to the database [18:06] this really saves a lot of boiler plate code you would need to write in other frameworks [18:07] we can also pass the bound form instance to a template for display [18:07] run "bzr pull -r tag:day3.7" [18:08] now you will have 2 new files, classroom/templates/session_view.html and classroom/templates/course_view.html [18:09] they are largely the same, so we'll just look at classroom/templates/session_view.html for now [18:09] by default, forms will render themselves as a series of table rows and cells [18:10] you can also use {{form.as_p}} or {{form.as_ul}} to have it render as a series of

or

  • tags [18:11] notice that it does not create the
    or tags itself, this makes it easy to add thing before or after the form fields [18:11] such as submit and reset buttons [18:11] now if you look at classroom/views.py you will see 2 new view functions [18:12] session_view and course_view [18:12] both take an additional id argument [18:12] again, the view functionality is similar between them, so we'll only look at session_view [18:13] first we determine if the session_id is 0, this lets us know if we are creating a new session, or modifying an existing one [18:13] if it's 0, we just create an empty Session() instance [18:14] otherwise, we use the helper method get_object_or_404 to see if a session by that id exists in the database [18:14] if it doesn't exist, this method will raise an exception, and Django will forward the request to an error page saying that the Session for that id can't be found [18:14] next, we check to see if this is an HTTP POST request. [18:15] if it is a POST, then we need to process the submitted values from our form [18:15] we create a new SessionForm, bound to both the Session instance we created and the POST data that was submitted [18:16] calling is_valid() will return true if all of the form's fields contain good data [18:16] if it's all good, we call save() to apply the data to the record in the database [18:16] if it's not a POST request, we are just going to display the form to the user [18:17] we create a SessionForm that is only bound to the session instance [18:17] and add that to a Context object for our template [18:17] and then call render_to_response again [18:18] finally, let's look at urls.py [18:18] we added 2 new entries there, for session_view and course_view [18:19] these use a slightly more complex url pattern than we've previously used, so lets take a closer look at them [18:19] r'^session/(?P\d+)' [18:19] this will match /session/#, where # can be any digit [18:19] or multiple digists [18:19] as long as it's a number [18:19] 123 works [18:19] 12abc doesn't [18:20] it then assigns the matching value to session_id, which is passed to the view function [18:20] so, to view session #1, use the url /session/1 [18:20] to view session #2, use /session/2 [18:20] and to get a form for creating a new session, use /session/0 [18:20] that is because our view is treating session_id==0 as a special case [18:21] ryanprior45 asked: where do I type /session/1 ? [18:21] sorry, that would be at the end of your URL, like http://127.0.0.1:8000/session/1 [18:22] hemanth asked: does dw+ match words and digit, is it the same logic? [18:22] any valid regular expression will work [18:23] \d\w+ would work [18:23] to match 1abc [18:23] for example [18:24] we also added links to the schedule view to take you to the correct URL [18:24] it should look something like this: http://www.growingupfree.org:8001/schedule/ [18:25] if you look at classroom/templates/schedule_view.html you will see how we're doing this [18:26] now, since views are mapped to url patterns in urls.py, you won't necessarily know what the URL pattern will look like when you're writing your templates and views [18:27] to accomodate this, Django has a powerful reverse-lookup ability that when given the name of a view, can give you a URL that it is mapped to [18:28] so we use {% url session_view session.id %} to lookup a URL pattern named "session_view" that takes a single argument [18:28] Django then locates the URL pattern, and inlines the argument value you passed [18:28] so {% url session_view session.id %} will return /session/1 if session.id==1 [18:29] if we wanted to change you URL pattern to '^classroom/session/(?P\d+)' [18:29] then {% url %} would return /classroom/session/1 [18:30] this makes Django applications very portable, as you can write them without knowing (or caring) how the urls will ultimately be mapped in a given project [18:30] any other questions on what we just covered? [18:31] alright [18:32] now, while Django is smart enough to validate the format of data, it's doesn't know enough to determine if the value makes logical sense [18:32] for example, when adding a new Session, it wouldn't make sense to create one with an end_time in the past [18:33] you can do this in your view, but if the validation is something that should always exist, it's best to add it to the Form [18:33] that way, is_valid() will run your custom validations, and return false if any one of them raises an exception [18:34] run "bzr pull -r tag:day3.8.1" [18:34] now if you look at classroom/forms.py you will see that I've added a method to our Form classes [18:35] Django looks for methods named "clean_$fieldname" for each $fieldname in the form [18:35] since we are using ModelForms, that means for every field name in the Model itself [18:36] so when it is validating the format of the data in 'end_time', it will look for a method called 'clean_end_time', and call it for the final cleaned value [18:37] in there, I retrieve the current clean value for the field, and if the Session is new, check that the 'end_time' value exists sometime in the future [18:38] if it doesn't, an exception will be raised. is_valid will return false, and the user will be directed back to the form page, only now the text of the exception will be displayed above the 'end_time' field [18:38] now, that's an easy way for validating an field's value by itself [18:39] but often times you need to validate a field's value when compared to another field's value [18:39] for example, it wouldn't make sense for a session's end_time to come before it's start_time [18:40] to do that, we need to implement a 'clean()' method on the form, that will be called after all the 'clean_$fieldname' methods are called [18:40] run "bzr pull -r tag:day3.8.2" [18:41] and "bzr diff -r tag:day3.8.1" [18:41] now you will see the clean() method on each of our Form classes [18:41] the first thing I do is get the values for start_time and end_time [18:42] notice that I'm using self.cleaned_data.get('start_time'), rather than self.cleaned_data['start_time'] [18:42] that's because of 'start_time' failed validation, it won't be added to self.cleaned_data, but Django will continue to call all clean and clean_fieldname methods anyway [18:43] so we need to account for that field not existing in cleaned_data [18:43] calling cleaned_data.get('start_time') will return None if it doesn't exist [18:43] calling cleaned_data['start_time'] will raise a KeyError [18:44] next I check that both 'start_time' and 'end_time' are not None, and then if 'end_time' comes before 'start_time', raise an exception [18:45] don't forget to return self.cleaned_data, otherwise you won't be able to use it in your view, or when calling form.save [18:46] any questions so far on forms validation? [18:47] alright [18:47] in tag:day3.9 I just added an extra table to the schedule view for listing courses, there isn't really anything in it that we haven't already discussed [18:48] by now, we can see our data, we can add data, and we can modify data [18:48] but, we can't delete it! [18:48] it's time to implement that [18:48] run "bzr pull -r tag:day3.10" [18:49] you will get the new files classroom/templates/session_delete.html and classroom/templates/course_delete.html [18:49] you will also get 2 new view fuctions in classroom/views.py [18:49] let's look at those first [18:50] since we don't need to think about new (unsaved) records when deleting, we just call get_object_or_404 to retrieve the instance of the record we want to delete [18:51] now, it's polite to double check that the user wanted to actually delete something [18:51] it's also good policy to not delete things on a GET request [18:52] so, if it is a GET request, we first show them a page (course_delete.html or session_delete.html) to warn them about what they are about to do [18:52] this page builds a that will POST back to the view if the user confirms the deleting by clicking the submit button [18:53] when the view sees a POST method, it will then delete the instance from the database, and redirect the user back to the schedule view [18:53] if you look at the view templates for Session and Course, you will see the link added to the bottom to delete that instance [18:54] by now your application should look like this: http://www.growingupfree.org:8001/schedule/ [18:54] you can add, edit and delete your sessions and courses to your heart's content [18:54] and in only 4 hours time too! [18:55] now, there is still much to implement, like access control, a better looking template, etc [18:56] and I plan to offer more classes in this series to cover those topics after Ubuntu Opportunistic Developer Week (all of next week) [18:56] this is also a live project: https://launchpad.net/classroom-scheduler [18:56] so development will certainly continue on it, even after these classes are over [18:57] well, my time is almost over, are there any questions on today's classes, or any previous ones? [18:59] any suggestions or wants for future classes? [18:59] okay, well the IRC logs are linked on the launchpad page now [19:00] and as always, you can email me if you have any other questions; mhall119@ubuntu.com [19:00] hemanth asked: pre preparation list for the class? {suggestion} [19:00] I only had a basic syllabus outline this time === ChanServ changed the topic of #ubuntu-classroom to: Welcome to the Ubuntu Classroom - http://wiki.ubuntu.com/Classroom || Support in #ubuntu || Upcoming Schedule: http://is.gd/8rtIi [19:01] I will try to prepare a list of required apps to have installed before future classes [19:01] is that what you were asking for? [19:01] mhall119|work: I'm sorry, where do you say are the logs? [19:01] https://launchpad.net/classroom-scheduler has links to all 4 hours [19:01] hemanth: I will make sure I have better setup material next time [19:02] mhall119|work: and.. is there a wiki? [19:02] mhall119|work, ok :) [19:02] now you guys have a working, useful Django application, start hacking on your own now! [19:02] malev: only a syllabus outline: https://wiki.ubuntu.com/mhall119/classes/LearningDjango [19:03] mhall119|work: cool!! thanks!