=== 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 | ||
* shadeslayer is excited about tomorrow | 13:04 | |
=== eXe is now known as Guest23458 | ||
=== Igorots is now known as Knightlust | ||
cjohnston | tomorrow? | 14:13 |
---|---|---|
shadeslayer | cjohnston: eh? | 14:15 |
shadeslayer | cjohnston: oh ill be giving a packaging session ;) | 14:15 |
cjohnston | gotcha | 14:15 |
shadeslayer | :D | 14:16 |
Pendulum | cjohnston: and hggdh is giving a bug q&a | 14:16 |
cjohnston | I knew about hggdh's | 14:16 |
cjohnston | I knew about the PPA thing too.. | 14:16 |
cjohnston | just fergot about it | 14:16 |
Pendulum | heh | 14:16 |
cjohnston | since I put it on the calendar.. | 14:17 |
cjohnston | lol | 14:17 |
shadeslayer | :P | 14:17 |
cjohnston | shadeslayer: PM... | 14:17 |
shadeslayer | im just writing notes write now on it | 14:17 |
hggdh | what happens with me? | 15:18 |
cjohnston | hggdh: you are doing a bug q&a later | 15:32 |
hggdh | cjohnston: yes, indeed. Adn you wanted to talk with me | 15:32 |
cjohnston | yup.. Got a few? | 15:33 |
hggdh | yes | 15:35 |
cjohnston | PM hggdh | 15:35 |
=== 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 | ||
mhall119|work | hello and good afternoon | 17:01 |
mhall119|work | today we're going to do a double class, since I had to cancel yesterday | 17:01 |
mhall119|work | for those of you who misses yesterday, logs from the first two sessions can be found here: https://launchpad.net/classroom-scheduler | 17:02 |
mhall119|work | A quick recap of what we covered yesterday... | 17:02 |
mhall119|work | 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:03 |
mhall119|work | we also configured the Django Admin application so we can view our Models and create instances of them in the database | 17:04 |
mhall119|work | 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:04 |
mhall119|work | now, before we begin, I made some changes to the application that are going to require you to modify your database | 17:05 |
mhall119|work | If you go to https://wiki.ubuntu.com/mhall119/classes/LearningDjango and scroll down to the section for Day 3 | 17:06 |
mhall119|work | you will see my note | 17:06 |
mhall119|work | I'll briefly explain what changes I made and why | 17:06 |
mhall119|work | "class" is a python keyword for defining, what else, classes | 17:07 |
mhall119|work | 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:07 |
mhall119|work | so, I renamed the application to "classroom" to avoid the conflict | 17:08 |
mhall119|work | however, when django created tables in the database, it defaults to naming them "application_model" | 17:08 |
mhall119|work | by changing the name of the application, I also changed the name of the tables it will use | 17:08 |
mhall119|work | so, once you run "bzr pull -r tag:day3.1" | 17:09 |
mhall119|work | 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 |
mhall119|work | I'll give a few minutes for everyone to get their local copy updated to day3.1 | 17:10 |
mhall119|work | ask a QUESTION: if you have trouble with this | 17:11 |
mhall119|work | if you update your old database, go into the admin and create a session and course for testing | 17:12 |
mhall119|work | alright, time to get started | 17:13 |
mhall119|work | day3.1 brought in several updates | 17:13 |
mhall119|work | it creates a new view method "schedule_view" | 17:14 |
mhall119|work | along with templates/schedule_view.html | 17:14 |
mhall119|work | and wired them up to the url /schedule/ | 17:14 |
mhall119|work | if you again run "python manage.py runserver" you should see something like this: http://www.growingupfree.org:8001/schedule/ | 17:15 |
mhall119|work | let's look into classroom/views.py | 17:16 |
mhall119|work | as you can see, schedule_view is very simple | 17:16 |
mhall119|work | Session.objects.filter() returns a list of Session objects from the database | 17:17 |
mhall119|work | you can add key/value pairs to filter() to restrict what is returned, we'll see that in action later | 17:17 |
mhall119|work | next it creates a Context instance, and adds the list of sessions to it | 17:17 |
mhall119|work | it then calls render_to_response, passing it the name of the template and the context | 17:18 |
mhall119|work | render_to_response is a utility method that will render the template into an HttpResponse object for us | 17:18 |
mhall119|work | now look at classroom/templates/schedule_view.html | 17:19 |
mhall119|work | Django templates are HTML with additional markup | 17:19 |
mhall119|work | {% %} blocks are "tags" that perform logic operations | 17:20 |
mhall119|work | {{ }} blocks will inline the value of the contained expression into the HTML | 17:20 |
mhall119|work | 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 |
mhall119|work | you'll also notice that the {{ }} blocks let you properties of a variable as well | 17:21 |
mhall119|work | so {{ session.course.title }} will try to get the title of the course this session is a part of | 17:22 |
mhall119|work | any questions so far? | 17:22 |
mhall119|work | alright, moving on | 17:23 |
mhall119|work | run: "bzr pull -r tag:day3.2" | 17:24 |
mhall119|work | then "bzr diff -r tag:day3.1" | 17:24 |
mhall119|work | here you can see that we are using the {% if %}{% else %}{% endif %} blocks | 17:25 |
mhall119|work | then do exactly what you would expect | 17:25 |
mhall119|work | {% if value %} will be true if value has a non-false value in python | 17:26 |
mhall119|work | so, None, 0 and empty lists will execute the {% else %} block | 17:26 |
mhall119|work | {% else %} is optional | 17:26 |
mhall119|work | now we are currently returning the datatime as python's default string representation | 17:27 |
mhall119|work | 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 |
mhall119|work | if you run "bzr pull -r tag:day3.3", then "bzr diff -r tag:day3.2" | 17:28 |
mhall119|work | you will see that i split it into 2 columns, and use template "filters" to format what is displayed | 17:29 |
mhall119|work | again, Django provides a number of built in filters such as "date" | 17:29 |
mhall119|work | filters use the syntax: {{variable|filter:"extra"}} | 17:30 |
mhall119|work | the :"extra" is optional, not all filters need it | 17:30 |
mhall119|work | filters are used to act on the value of a variable, and return a different result | 17:31 |
mhall119|work | Now you should be seeing something like this: http://www.growingupfree.org:8001/schedule/ | 17:31 |
mhall119|work | we're starting to look a lot more like the wiki schedule now | 17:31 |
ClassBot | hemanth asked: can the filtering of time and date be done as per the clients local time? | 17:32 |
mhall119|work | 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 |
mhall119|work | now, looking back at https://wiki.ubuntu.com/Classroom#Schedule | 17:33 |
mhall119|work | you will see that their times are links to an external site that does give times in several locations | 17:34 |
ClassBot | hemanth asked: can't we get the location of the client and do UTC +/- ? | 17:34 |
mhall119|work | yes, and in fact python has a helpful method of doing this in datetime.timedelta | 17:35 |
mhall119|work | alright, so next we want to turn out time display into a link | 17:35 |
mhall119|work | 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 |
mhall119|work | instead, we're going to make our own template filter to do it | 17:36 |
mhall119|work | run "bzr pull -r tag:day3.4" | 17:36 |
mhall119|work | and then "bzr diff -r tag:day3.3" | 17:37 |
mhall119|work | you will see the new file classroom/templatetags/scheduler_tools.py | 17:37 |
mhall119|work | this a file that will contain any number of custom tags and filters | 17:38 |
mhall119|work | we use {% load scheduler_tools %} to include them in our schedule_view.html template | 17:38 |
mhall119|work | then we can get the URL for our link by simply using {{session.start_time|timelink}} | 17:39 |
mhall119|work | if you look at classroom/templatetags/scheduler_tools.py, you see that filters are pretty easy to write | 17:39 |
mhall119|work | they are basically a function that takes the associated variable as an argument (and optionally any "extra" values) | 17:40 |
=== edsoncanto is now known as ecanto | ||
mhall119|work | and returns a value to be inlined into the HTML | 17:40 |
=== ecanto is now known as edson | ||
=== yofel_ is now known as yofel | ||
mhall119|work | @register.filter is a python decorator that registers your filter with Django when {% load %} is called on the file | 17:41 |
mhall119|work | now, just as Filters are exceptionally easy to write, Tags are quite hard | 17:41 |
mhall119|work | which is why I am not covering them in this series | 17:41 |
mhall119|work | but if you are curious, you can view them in the Django source files to see how they are implemented | 17:42 |
mhall119|work | any questions at this point? | 17:42 |
mhall119|work | anyone need a break before we get started on the next hour? | 17:43 |
ClassBot | lsteeger asked: yes. how about 5 mins? | 17:44 |
mhall119|work | okay, we'll take a short break then | 17:45 |
mhall119|work | okay, is everyone back? | 17:52 |
mhall119|work | alright | 17:53 |
mhall119|work | time to get into forms! | 17:53 |
mhall119|work | much like a model, a Form is a collection of Field instances that map python values to HTTP parameters | 17:54 |
mhall119|work | 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 |
mhall119|work | form fields will validate their own content, and raise an exception of it can not be converted into the right python data type | 17:55 |
mhall119|work | for example, if you pass "abc" to an IntegerField, it will raise an exception | 17:56 |
mhall119|work | you call form.is_valid() to determine if all the fields in a form contain well formatted values | 17:57 |
mhall119|work | you can then get the corresponding python data type from form.cleaned_data[] | 17:57 |
mhall119|work | Django form fields also contain widgets that are used to create the HTML necessary to build the form | 17:58 |
mhall119|work | so you don't even have to worry about that | 17:58 |
mhall119|work | 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 |
mhall119|work | however, you won't actually have to explicitly use them all the time | 17:59 |
=== 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 | ||
mhall119|work | Django provides a helpful ModelForm type, that will create a Form class with all the fields | 18:01 |
mhall119|work | in your Model | 18:01 |
mhall119|work | run "bzr pull -r tag:day3.6" | 18:01 |
mhall119|work | and "bzr diff -r tag:day3.4" | 18:02 |
mhall119|work | (yes, day3.5 is missing) | 18:02 |
mhall119|work | now, you will see a new file: classroom/forms.py | 18:02 |
mhall119|work | this file contains 2 very small classes, SessionForm and CourseForm, both of which extend Django's ModelForm | 18:03 |
mhall119|work | you will also notice that, just like your Models, it contains an internal Meta class | 18:03 |
mhall119|work | in this class, we specify which of our Model definitions we want this Form class to use | 18:03 |
mhall119|work | and that's really all we need | 18:04 |
mhall119|work | we can now call form = SessionForm(request.POST) | 18:05 |
mhall119|work | we can also bind an instance of our model to the form: form = SessionForm(instance=mysession, data=request.POST) | 18:06 |
mhall119|work | 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 |
mhall119|work | this really saves a lot of boiler plate code you would need to write in other frameworks | 18:06 |
mhall119|work | we can also pass the bound form instance to a template for display | 18:07 |
mhall119|work | run "bzr pull -r tag:day3.7" | 18:07 |
mhall119|work | now you will have 2 new files, classroom/templates/session_view.html and classroom/templates/course_view.html | 18:08 |
mhall119|work | they are largely the same, so we'll just look at classroom/templates/session_view.html for now | 18:09 |
mhall119|work | by default, forms will render themselves as a series of table rows and cells | 18:09 |
mhall119|work | you can also use {{form.as_p}} or {{form.as_ul}} to have it render as a series of <p> or <ul><li> tags | 18:10 |
mhall119|work | notice that it does not create the <form> or <table> tags itself, this makes it easy to add thing before or after the form fields | 18:11 |
mhall119|work | such as submit and reset buttons | 18:11 |
mhall119|work | now if you look at classroom/views.py you will see 2 new view functions | 18:11 |
mhall119|work | session_view and course_view | 18:12 |
mhall119|work | both take an additional id argument | 18:12 |
mhall119|work | again, the view functionality is similar between them, so we'll only look at session_view | 18:12 |
mhall119|work | 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 |
mhall119|work | if it's 0, we just create an empty Session() instance | 18:13 |
mhall119|work | otherwise, we use the helper method get_object_or_404 to see if a session by that id exists in the database | 18:14 |
mhall119|work | 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 |
mhall119|work | next, we check to see if this is an HTTP POST request. | 18:14 |
mhall119|work | if it is a POST, then we need to process the submitted values from our form | 18:15 |
mhall119|work | we create a new SessionForm, bound to both the Session instance we created and the POST data that was submitted | 18:15 |
mhall119|work | calling is_valid() will return true if all of the form's fields contain good data | 18:16 |
mhall119|work | if it's all good, we call save() to apply the data to the record in the database | 18:16 |
mhall119|work | if it's not a POST request, we are just going to display the form to the user | 18:16 |
mhall119|work | we create a SessionForm that is only bound to the session instance | 18:17 |
mhall119|work | and add that to a Context object for our template | 18:17 |
mhall119|work | and then call render_to_response again | 18:17 |
mhall119|work | finally, let's look at urls.py | 18:18 |
mhall119|work | we added 2 new entries there, for session_view and course_view | 18:18 |
mhall119|work | these use a slightly more complex url pattern than we've previously used, so lets take a closer look at them | 18:19 |
mhall119|work | r'^session/(?P<session_id>\d+)' | 18:19 |
mhall119|work | this will match /session/#, where # can be any digit | 18:19 |
mhall119|work | or multiple digists | 18:19 |
mhall119|work | as long as it's a number | 18:19 |
mhall119|work | 123 works | 18:19 |
mhall119|work | 12abc doesn't | 18:19 |
mhall119|work | it then assigns the matching value to session_id, which is passed to the view function | 18:20 |
mhall119|work | so, to view session #1, use the url /session/1 | 18:20 |
mhall119|work | to view session #2, use /session/2 | 18:20 |
mhall119|work | and to get a form for creating a new session, use /session/0 | 18:20 |
mhall119|work | that is because our view is treating session_id==0 as a special case | 18:20 |
ClassBot | ryanprior45 asked: where do I type /session/1 ? | 18:21 |
mhall119|work | sorry, that would be at the end of your URL, like http://127.0.0.1:8000/session/1 | 18:21 |
ClassBot | hemanth asked: does dw+ match words and digit, is it the same logic? | 18:22 |
mhall119|work | any valid regular expression will work | 18:22 |
mhall119|work | \d\w+ would work | 18:23 |
mhall119|work | to match 1abc | 18:23 |
mhall119|work | for example | 18:23 |
mhall119|work | we also added links to the schedule view to take you to the correct URL | 18:24 |
mhall119|work | it should look something like this: http://www.growingupfree.org:8001/schedule/ | 18:24 |
mhall119|work | if you look at classroom/templates/schedule_view.html you will see how we're doing this | 18:25 |
mhall119|work | 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:26 |
mhall119|work | 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:27 |
mhall119|work | so we use {% url session_view session.id %} to lookup a URL pattern named "session_view" that takes a single argument | 18:28 |
mhall119|work | Django then locates the URL pattern, and inlines the argument value you passed | 18:28 |
mhall119|work | so {% url session_view session.id %} will return /session/1 if session.id==1 | 18:28 |
mhall119|work | if we wanted to change you URL pattern to '^classroom/session/(?P<session_id>\d+)' | 18:29 |
mhall119|work | then {% url %} would return /classroom/session/1 | 18:29 |
mhall119|work | 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 |
mhall119|work | any other questions on what we just covered? | 18:30 |
mhall119|work | alright | 18:31 |
mhall119|work | 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 |
mhall119|work | for example, when adding a new Session, it wouldn't make sense to create one with an end_time in the past | 18:32 |
mhall119|work | 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 |
mhall119|work | that way, is_valid() will run your custom validations, and return false if any one of them raises an exception | 18:33 |
mhall119|work | run "bzr pull -r tag:day3.8.1" | 18:34 |
mhall119|work | now if you look at classroom/forms.py you will see that I've added a method to our Form classes | 18:34 |
mhall119|work | Django looks for methods named "clean_$fieldname" for each $fieldname in the form | 18:35 |
mhall119|work | since we are using ModelForms, that means for every field name in the Model itself | 18:35 |
mhall119|work | 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:36 |
mhall119|work | 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:37 |
mhall119|work | 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 |
mhall119|work | now, that's an easy way for validating an field's value by itself | 18:38 |
mhall119|work | but often times you need to validate a field's value when compared to another field's value | 18:39 |
mhall119|work | for example, it wouldn't make sense for a session's end_time to come before it's start_time | 18:39 |
mhall119|work | 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 |
mhall119|work | run "bzr pull -r tag:day3.8.2" | 18:40 |
mhall119|work | and "bzr diff -r tag:day3.8.1" | 18:41 |
mhall119|work | now you will see the clean() method on each of our Form classes | 18:41 |
mhall119|work | the first thing I do is get the values for start_time and end_time | 18:41 |
mhall119|work | notice that I'm using self.cleaned_data.get('start_time'), rather than self.cleaned_data['start_time'] | 18:42 |
mhall119|work | 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:42 |
mhall119|work | so we need to account for that field not existing in cleaned_data | 18:43 |
mhall119|work | calling cleaned_data.get('start_time') will return None if it doesn't exist | 18:43 |
mhall119|work | calling cleaned_data['start_time'] will raise a KeyError | 18:43 |
mhall119|work | 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:44 |
mhall119|work | 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:45 |
mhall119|work | any questions so far on forms validation? | 18:46 |
mhall119|work | alright | 18:47 |
mhall119|work | 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:47 |
mhall119|work | by now, we can see our data, we can add data, and we can modify data | 18:48 |
mhall119|work | but, we can't delete it! | 18:48 |
mhall119|work | it's time to implement that | 18:48 |
mhall119|work | run "bzr pull -r tag:day3.10" | 18:48 |
mhall119|work | you will get the new files classroom/templates/session_delete.html and classroom/templates/course_delete.html | 18:49 |
mhall119|work | you will also get 2 new view fuctions in classroom/views.py | 18:49 |
mhall119|work | let's look at those first | 18:49 |
mhall119|work | 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:50 |
mhall119|work | now, it's polite to double check that the user wanted to actually delete something | 18:51 |
mhall119|work | it's also good policy to not delete things on a GET request | 18:51 |
mhall119|work | 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 |
mhall119|work | this page builds a <form> that will POST back to the view if the user confirms the deleting by clicking the submit button | 18:52 |
mhall119|work | 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 |
mhall119|work | 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:53 |
mhall119|work | by now your application should look like this: http://www.growingupfree.org:8001/schedule/ | 18:54 |
mhall119|work | you can add, edit and delete your sessions and courses to your heart's content | 18:54 |
mhall119|work | and in only 4 hours time too! | 18:54 |
mhall119|work | now, there is still much to implement, like access control, a better looking template, etc | 18:55 |
mhall119|work | 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 |
mhall119|work | this is also a live project: https://launchpad.net/classroom-scheduler | 18:56 |
mhall119|work | so development will certainly continue on it, even after these classes are over | 18:56 |
mhall119|work | well, my time is almost over, are there any questions on today's classes, or any previous ones? | 18:57 |
mhall119|work | any suggestions or wants for future classes? | 18:59 |
mhall119|work | okay, well the IRC logs are linked on the launchpad page now | 18:59 |
mhall119|work | and as always, you can email me if you have any other questions; mhall119@ubuntu.com | 19:00 |
ClassBot | hemanth asked: pre preparation list for the class? {suggestion} | 19:00 |
mhall119|work | I only had a basic syllabus outline this time | 19:00 |
=== 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 | ||
mhall119|work | I will try to prepare a list of required apps to have installed before future classes | 19:01 |
mhall119|work | is that what you were asking for? | 19:01 |
malev | mhall119|work: I'm sorry, where do you say are the logs? | 19:01 |
mhall119|work | https://launchpad.net/classroom-scheduler has links to all 4 hours | 19:01 |
mhall119|work | hemanth: I will make sure I have better setup material next time | 19:01 |
malev | mhall119|work: and.. is there a wiki? | 19:02 |
hemanth | mhall119|work, ok :) | 19:02 |
mhall119|work | now you guys have a working, useful Django application, start hacking on your own now! | 19:02 |
mhall119|work | malev: only a syllabus outline: https://wiki.ubuntu.com/mhall119/classes/LearningDjango | 19:02 |
malev | mhall119|work: cool!! thanks! | 19:03 |
Generated by irclog2html.py 2.7 by Marius Gedminas - find it at mg.pov.lt!