 getting towards the end of the conference. And glad to see some people are still attending sessions. Before I get into the talk, I wanted to just discuss a little bit about the evolution of the talk. The originally, I envisioned this as a slightly different talk as then it turned into. I was going to say, here's what the admin is good for. And here's some cool things you can do to the admin. And you're going to run into some limits for the admin. And once you do that, here's some tools you need to use. Here's how you look to grow beyond the admin. As it turned out, as I was developing the talk, that first part became too big to allow for the second part. So the second part of growing beyond the admin is very limited in this talk. And this talk mainly focuses on really what the admin is good for and how to make it better than the defaults and how to configure it to be a really excellent tool for administration of data. Background on me. I have been using Django since 2006. I came to Django via a personal project. I had a crossword database. I build crosswords. I construct crosswords. And I had been doing that for several years. And I had to master database of puzzles that I used to help me in constructing. And I wanted a web ferment for that database. And I came to Django to learn how to do that. And also to learn Python and web development. More recently, I've been using Django as a personal project. I'll call it Cat Tracker. In 2010, I started volunteering at a cat rescue. At that time, the cat rescue was fairly new. And as I grew in the cat rescue from being just a foster home, I started doing administrative work for them. And I found that they were tracking the cats that they had. And who had adopted the cats and the whole history via a set of spreadsheets. And this was fine when you had 25, 30 cats, when you started to have hundreds of cats in your history and hundreds of cats in the program. That was going to be impossible. So I converted that into a database. And it's a Django database. And the administration has done via Django admin. So I've used a lot of admin customization there. And I'll pull from that project, examples from that project a lot in this presentation. When I started working with Django, I did start working with, at that point in time, Django was pre-1.0. They were doing some backwards and compatible changes. New forms was new, wasn't done yet. The new forms admin branch was in development. And I was using some features of admin that I wasn't sure they were going to maintain. And I wanted to make sure they maintained them. So I started playing with that. And I started getting involved in Django and make sure that it was growing away and it was going to keep the features I wanted. So I did get involved in the development of Django. And I have been a core developer in Django since 2008. I also, in 2010, started working for Cactus Consulting Group. We build websites for clients. And over the course of four years, I've probably worked on a dozen different websites and used admin in varying ways, depending on the budget and the needs of the clients. So I've used a lot of different admin features over the years. What is admin? Admin has been referred to you as Django's killer app. It's this cool application you get out of the box. You don't have to do anything, and pretty much anything. And you can just administer your data. You just define your models and maybe some minor configuration. And you have a web interface to your data. I refer to it as an easy bread interface. A lot of people refer to this as a crud interface. I like the word bread better. Bread stands for browse, read, edit, add, delete. Those are all the things that the admin focuses and can do really well. And it has this nice classic login screen. If you just bring up a Django 1.7 project and run start project and create a super user and go to the admin URL, you get this login. If you log in, you get an index page. What this is showing you is the list of applications you have in your site. And for each application that has registered models, it's shown and the models that that application has registered are shown there that you can do something with. And you can click through into one of those. In this case, I've clicked into the users and I can see I've created a single super user myself. And on this page, this is the list view for a model. And you can start seeing that there's some advanced capabilities that admin gives you. There's a search box up there. There is an actions drop down and there's an ability to select items on the left. So obviously there's ways to say, I wanna select these objects and I wanna do something with them. On the right side, you've got filters that you can specify that you can filter down your objects based on whether they're staff, whether they're super user. So you can start to see that there's some advanced capabilities that admin brings to you. If you click through into a user, you get to the edit page for a user. And again, here you can see that admin provides some ability to make it prettier than just a default spewing out of fields. Because the fields for the user here have obviously been grouped into areas. There's the basic user information password and there's personal information in the name and then there's permission. So there's some customization here done to group the fields of a user in a reasonable way for the administrator to edit. And you can also see that for the password field, for example, something special has been done because it's not showing you the actual password value, but it's showing you some information about the password. So that's your basic admin that you get out of the box. One of the things I wanted to focus on in this talk, which I do the way it grew, it didn't turn into this as much as I wanted, but I wanted to focus on who should use the admin and who should be exposed to the admin. And in my opinion, I'll still go through what I think here. I do think developers should use the admin to whatever extent it's useful for them in developing their code. And I do think developers should definitely understand the admin's capabilities and how it can be used to configure, how it can be configured to be more helpful to people. And primarily that's for site administrators because the admin can be very useful for site administrators, but if you just give the site administrators a raw, unconfigured admin interface, you're gonna present them with a bunch of stuff that they don't necessarily know what it is. You've got ancillary models and packages that you've installed, perhaps, that have nothing to do with their business needs, but they're there for the site for some reason. And if you show that stuff to them, that's probably gonna confuse them. And if you just show them raw dumps of fields that fill out or whatever, that's probably not gonna be very friendly to them. So I think developers should understand the capabilities of admin so that they can configure the admin for the site administrators to show the models that make sense to the site administrators in a way that makes sense to the site administrators. And that's a lot of what this talk will focus on. In my opinion, I don't think admin really should be opened up to end users. I don't think most sites should be built around admin. Most sites have way more complicated things that they need to do than browse, read, edit, and delete. And if you're gonna build a site, I think you should build a site and not try and grow admin into a more advanced site. So let's start with some basic easy customization you can do with admin now. On those previous screens, what I showed was full of Django administration, the login screen said Django administration, and all the index pages of Django everywhere, which you can do when you build a site to say, actually, it's not Django administration, it's my site administration. So for my cross-repellent database, for example, there's three variables you can set just by importing admin from Django Contrib, set the site header, site title, index title to something specific to my site, and suddenly I no longer have, oh, I forgot to mention, this easy part is new in Django 1.7. Previously you had to copy an admin template and configure it. So this easy part is new in Django, so you could do it in the past, but it's a little easier now. So once you do it, you get a nice custom login. Of course, anyone who looks at that's gonna say, that's Django, even though it doesn't say it anymore, it's obviously looks like Django, but it does identify your site. Can you all hear me when I'm not echoing like that? Okay. Not too well. And not too well over there? That's good. I'm gonna turn this way, okay. Worse or better? Actually, I should probably turn this way, move it that way. Is it upside down? Maybe I'll just talk on this side. Anyway, the index page now has been configured and has the crossword database stuff on the top, on the title. Once you get beyond that basic level of customization, you need to start talking about how you want your models to appear in Django, and in order to describe that to admin, what you're gonna wind up doing is talking about model admin objects. And what a model admin object is, is the description of a model, representation of a model in admin. Django admin provides a basic model admin that can introspect your model and provide default representation of a model, but you can also configure that to be more tailored to your needs. And that's what a lot of this talk will be about. These are defined in admin.py, and they are loaded at startup, and part of the code in there is gonna register the models to admin, and they have many, many, many options. And what I'm gonna try and do in this talk is focus on the ones that I find to be the most useful and organize them in a way to talk about here's options used for finding data and filtering data and seeing data, here's options for editing data, here's options for doing bulk editing of data, and here's ways to control access to data in the admin. So I'm gonna try and group my discussion of the admin options that I talk about. So I'll start with, whoops, not bad. I'll start with, this is the beginning of the model admin definition for my cat model in the cat tracker, which as you might have, my guess is a fundamental model. And so all model admin objects are gonna inherit from, based off of admin.modeladmin, the normal naming convention is you take your model name and tag admin onto the end of it and that's what you call your model admin object for your particular model. In 1.7, there's a new decorator that lets you easily register models with the admin, so you can just say admin.register at the top. In older Django's, you need to do an admin.site.register to specify the model admin to use for each of your models. The first four things I'm gonna focus on are controlling how you see data, what you see about a list of cats and how you can filter and search on cats. So I'm gonna talk about list display, list filter, list editable and search fields. Those are the four things listed on there. And as you can see, most of them are lists or tuples or sequences of things and most of them are just strings. Some of them are can be code like that active foster homes filter, which we'll talk about a little bit more. And so with these four things, influencing what you see about cats, you get a page that looks like, hit the right button, this. So what you see here, list display is controlling what columns you see there. So you see the name of the cat, the estimated date of birth or the intake date of the cat, estimated date of birth. And then those are all pretty obviously simple fields on the model. Then you can get a little more complicated. The age of the cat, the altered date for the cat, those are not actually fields on the model, those are information about the instance that we've generated somehow. And altered date is a little bit weird looking because some of them are red and some of them are not. So there's some mystery behind that and there's some more fields. Available there is a checkbox because available was listed as list editable. So what we said there was list available is a field of the cat that I wanna be editable on the list page. So this is jumping ahead a little bit is a way to allow for some bulk editing capability where you can see a list of objects, change some values in those objects and save them in one go. If you page down to the bottom here, you can see that there was a save button at the bottom so you could change the availability of the cat right on the list page. At the top, you can see there's a search box and there's a string to say what's gonna be searched on for the cats. There's the actions, which we'll talk about later. On the right side, there's filters. Some of those are simple filters on fields of the model that admin provides by default. The active foster home filter is the final one down there. And you can see that there's a list of foster homes and on the right side of the foster home, there's a number, which is actually the number of cats already in that foster home. So that is some custom code to build a custom filter for this and we'll talk about why we do that and how easy that is to do. And there's ordering. You can see that that list is ordered by intake date that is highlighted there and you can click on the different headers and change the ordering, the presentation of the fields. First, I'll talk about list display and how you can do something more than a simple field value. It can be a simple field name, that some of them are simple field names, or it could be a callable and you can specify the callable in any number of different ways. You can import it and specify it in the list, you can specify it as a string that is a attribute, a method on the model or a method on the model admin. I tend to make the methods on the model just because I find them usually useful outside of admin, so it's useful to have them available. The altered date one is, this is the code that goes behind it, it's a method on the model so it gets the model instance as its self value and to answer the mystery of why some of them are red and some of them are not, a cat has an attribute on it, which is a spaniard or date. If that has been set, this method just returns that date formatted in month, day. If that has not been set, then this method returns HTML for the anticipated spaniard or date for the cat and the HTML is formatted is made to format that date as red. So the red indication is an indication to the administrator to say, hey, this cat doesn't actually have a spaniard or date recorded. This cat may need to have their spaniard scheduled, may need to have it recorded. So the red is an indication to the administrator, there's something needs to be done here. In terms of admin and admin's capabilities in order to allow you to include HTML in your method here and have it show up properly, I do need to tell admin that it's okay to render tags that I have included in my outputs. I say allow tags if it's true. I also tell admin that if the user wants to order on this field, you should order on the estimated date of birth that will kind of correspond to the altered date for a cat. And I can also control what gets displayed at the top of the column and I set that to alter date. So that's a very simple code to allow for controlling what you see in the list display. Moving on to list filter. Again, this may be a simple field on the model or it can be custom filter code. And it's really easy. It's not hard to write a custom filter. In this case, we need a custom filter because foster homes come and go. So over the course of six years in the rescue we've had lots and lots of foster homes. They're not all still active. We don't want them showing up in that list to choose from necessarily when you want to filter. You just want the ones that are active that have cats. And we don't want to delete foster homes because there are things that reference foster homes and we lose history about where cats had been if we deleted them. So we don't want to just leave them. We want them to just be able to mark them inactive and we still want to be able to filter on active foster homes in the list of cats. So it's easy to write these because admin has provided a base class that it uses internally for most of the filters. Simple list filter and you can just extend from simple list filter to write your own filter and the information you need to tell admin about your filter is what you want to call it. That's the title of the top foster home. You also say what query string parameter you want to use when one of those is selected. The effect is that the URL is modified to include the query string parameter with a value to say I want to filter on this query string key that has this value. So in this case I have FH as the key. You can make that whatever you want. You just need to make sure that it's not used by another filter on the same page. And then there are two methods, one of which is on this page. Lookups is the method that's gonna be called to generate that list to show and the two things it needs to include for every foster home in that list is the query string key's value for that one and the human readable representation of that foster home. So there's simple code that filters on all active foster homes and returns that to full of the human readable representation and the primary key. And then the other piece that you need to implement is the query set method, which says when a change list page is being presented, this is your chance to filter down the query set if your filter has been activated. And you can tell if your filter has been activated by checking the self.value that sees if there's a value for your query string key and if so returns it to you. And in this case, if it has been set, you can filter down to only cats who are in that foster home. So it's very simple to add useful filters that are helpful to administrators in narrowing down the data they're looking at. Moving on to search. Search fields was another list of fields. It began that could just be a simple field on your model. Also with search fields, you can use double underscores syntax to traverse to a related model and say, I also want to search not just the cat's name, but the adopter's name. So that can be helpful at times. Admin, I noticed on the page where I showed this that there was text description at the top that said what was being searched. And admin does not do that automatically. It would be kind of hard for admin to print out a list of things that are being searched. So the way in which, but personally, I find it useful to know what's getting searched. So I usually like to customize this. And the way in which you do that is not via, why do I keep hitting that key? The way in which you do this is you override an admin template. So the admin defines what templates are gonna be used for different operations, the changeless template, the edit template, and you can override those at the model level, at the application level, at the global site level. In this case, I've overridden the changeless template for the cat model, which is under templates admin. C-track is the application, cat is the model. And all I've done is extended from the base changeless and admin. And I've overridden the search block in that to include my text followed by what the admin would have produced ordinarily. So that was fairly simple to do. I think I did have to dig into the template and see, did it define a block that I could easily override? I don't think that's entirely well documented yet. The existence of the templates is documented. But the actual blocks in them and whether they define all the things that you need to easily override bits of the pages is not necessarily fully documented. So that is presenting data, filtering data, searching data. The next thing I'm gonna talk about is customizing the ability to edit data. So once you've found the object you wanna edit, how do you control what you can see, what you can edit and how it's presented? And the four things I'm gonna talk about are field sets, read-only fields, in-lines, and also, I'm not gonna talk about this in any detail, so I'll talk about it here. You can also just override the form completely that the admin uses. By default, admin's gonna create a model form based on your model. You can say instead you wanna use this other form and you can do whatever you want in that form. As an example of a way I've used that for the cats, oftentimes you're taking in a cat and you need to supply the estimated date of birth, but in fact, it's a lot easier to say this cat looks like it's four weeks old and not to have to go back and figure out, oh, four weeks ago, the date wasn't such and such. So I've changed most of the forms here where the user can specify either the estimated date of birth or the age of the cat and the form will automatically take either one of those and set the estimated date of birth field. So you can override the form entirely to do whatever is useful. And that's fairly simple to do. The complicated thing is what you might wanna put in the form and that depends on your own business logic. Field sets are the things that you specify to say, I want this group of fields and then this group of fields and I'm gonna name them such and such and I'm gonna name this group of fields such and such. And these can get to be a bit of a nightmare when you got a lot of them because the way you declarative definition of layout is just kinda gets to be a little bit gnarly when it gets very long. So these are specified as two tuples. It's a list of two tuples or a sequence of two tuples. The first element in the tuple is the name of the field set, which might be empty, like the top one is usually empty. And then the second element in the tuple is dictionary that describes the options for that field set. The dictionary has some keys, one of which is gonna be the fields to include in that field set. You can also include a classes key in that dictionary to influence the classes that are put on the HTML for that field set which can influence the presentation. And Django admin provides a couple classes. You could also extend the CSS for admin and provide your own classes to do other stuff if you wanted to. As an example, this is the beginning of the field sets for a cat. The top part has just basic information about the cat. Names, test dates, such and such. The second field set has a name of description. It has a different set of fields and it's also specified the classes. And it's specified the classes collapse. And what that does is say this field set, name description, is gonna have a link in it to allow you to show and hide that set of fields so you don't necessarily have to see the whole thing all the time. One thing to point out here and one of the reasons this gets to be a little bit hairy is you can, in that list of fields, that you're providing group things. So as an example, name, intake date and foster home are grouped within that list and the following fields are grouped within that list and what that tells the admin is try to put these on the same line rather than putting them one after the other. So that lets you pack more information into a single line rather than spreading it out vertically. But when you start doing that, you've got tuples within tuples, within dictionaries within tuples and you don't know whether you need to do a closed parent or a closed bracket, so that's why they get to be a little hairy at times. But they're useful for grouping the information to show to admins in a logical fashion. Read only fields, I'll combine an example here with inlines. What you can do with read only fields is say, I wanna show some information about this field which may actually be a field or it may not be a field, it may be just something about the instance, but I don't want to make it be editable. So it's not gonna show up in an HTML input of any form. It's just gonna be information that's displayed and since it is just gonna be displayed, you can instead of specifying a field, specify a callable that returns some information. Inlines, I'm talking about read onlys with inlines. Inlines are a way to say, I'm editing, in this case, a caretaker who may have adopted some cats. So a caretaker is a person who may have adopted some cats, maybe have foster home. And in this case, I'm gonna say, caretakers have information associated with them. They have phone numbers, they have email addresses, they may have multiple of those, they may have adopted cats. So you can see at the bottom, caretaker admin defines inlines and these are the things that are related to a caretaker that I wanna show inline with the caretaker and I want to potentially be able to add phone numbers for this caretaker, add email addresses for this caretaker, edit the ones that exist, delete the ones that exist. And similarly for cats they've adopted, I may wanna edit some of the information about cats that they've adopted. I've shown the example inline that I've shown is the one for cat, which is somewhat customized and shows some of the options you can specify. For an inline, you're gonna extend from admin.tabular inline or admin.stacked inline, that's just a different presentation format. The options for an inline are overlapping but not exactly the same as the options for a model admin. So in this case, you need to tell the inline what the model is that's related to the page you're putting it on, the fields that you wanna display. In this case, I listed just three fields, a very many fields on a cat that I wanna see in the context of looking at a caretaker who has adopted a cat. All I wanna see is the name of the cat, the adopted name of the cat, and their microchip information. And of those fields, the only one I wanna edit is the adopted name of the cat. So the other two I've specified as read only, so I'm not gonna be able to edit that information about a cat on a caretaker page. I've also specified maxNlm equals zero and this says, kinda sounds like it says I don't wanna see any but what it says is I don't wanna see any blank ones. I don't wanna add any here. But I do see the ones that already exist. And I've also specified I don't wanna be able to delete adopted cats from this page because that is not where we would do that. And the effect of that is a page like this. This is an edit page for a caretaker. You've got your basic caretaker fields up top. Those have not been particularly customized. Then lower down, you've got the inlines for the phone, numbers for this caretaker and the inlines for the emails. That has also not been very customized other than to say I don't want blank ones but I do want the ability to add so you can see there's a link for some JavaScript to add additional phone numbers or email addresses. I can also edit or delete any fields in those that I want. And then for the cat, all I can see is the cats they've adopted and their adopted name and their microchip information. And the adopted name is the only information I can change here. And the reason for this presentation from the administrator's point of view is this is the page that the person who does follow up on cats would be going to contact the adopter two weeks after adoption to say how are things going? Do you have any problems? Do you have any questions? And what have you named the cat so we can register the microchip? So on this page, that's the only information they should really be wanting to change is the adopted name of the cat. So that's the only thing that's made editable for them. So that gets a little into how admin allows you to do a little bit of workflow stuff. Kinda, but not really. But you can tailor the presentation of the object and the objects related to it based on what that page is gonna be used for. Moving on, this is, that's editing. Moving on to bulk editing. This is another thing that administrators often need to be able to do to create a bunch of things all at once without having to edit them all individually. We saw a little bit of bulk editing capabilities with the list editable. Admin actions are a second way of doing bulk editing and they're extremely flexible. Anything that follows the select a bunch of things, one or more things, choose an action and go do it can be done with admin actions. And admin actions are specified as a list of strings on model admin. Here, actions for cats include the ability to vaccinate cats or record the vaccination of cats, record the movement of cats, record the testing of cats. And I've shown an example of the vaccination cats. This is just a method on, you specify the string that is a method on the model admin. So it gets as parameters to itself, the model admin, the request and the query set which represents the query set of things that were selected for this action. If all you need to do is something like mark a blog entry publish, you could do that here and just return redirect to the change list page. If you need to get some more information out of the user or you want them to confirm what they've asked to do, generally what you do is redirect them to another page that handles doing that. So you take the query set you were given, figure out what all the primary keys are for that query set and pass them along to some other view, which is what this does. It just redirects to C-Track VaxCats method view. That's it. And the only other piece of information there is that the action name that shows up in the dropdown is gonna be VaxC selected cats. I'm not gonna show the code for what that does. I'll show the workflow of selecting is to select on the left and you can see that they're highlighted in yellow at the bottom. I've selected four cats for vaccination, selected the action of vaccinate and I'm about to say go ahead and do it. And then that view that I didn't show you the code for is a pretty standard form view that generates a form for the user to fill out that includes the date of when the cats were vaccinated and provides a choice of list of things, vaccinations they may have gotten. And then when that form is posted, you've chosen four cats, say it chooses two vaccinations, distemper and feline leukemia, it's gonna create eight vaccination records for those cats, which is a lot more convenient than having to enter them all manually. You can do anything you want in admin actions. You can upload CSV or XLS files and process them. They're incredibly flexible. Any workflow that says select, do something with that selection you can do with admin actions. It was a little fiddly to get this to look like it is part of admin. You kind of have to dig into the admin templates and see how do I make the breadcrumbs look that way. But it can be done. It kind of looks like it's part of admin. So that is bulk editing. Next we're gonna talk about access control. So I mentioned way at the beginning, in order to make the admin useful for your site administrators, it helps to limit them to see only those things that make sense to them. And one way of doing that is with the default Django permissions and groups that come by default with Django. So every model in Django has a read, not a read, but a change and edit, delete and add permissions. And those permissions are automatically available in admin. You can assign them to users or groups. I usually find it most convenient to assign them to groups so that I can change and then assign users to groups so that it's more convenient to update as the application grows. So in this case I've got a C-track admin group that has a set of permissions for the cat tracker application. And on the left you can see it. This group does not have a set of applications, doesn't have any permissions for a completely different application on the same site, the altered track program. That's a completely different program it has to do with vouchers, doesn't have to do with the cats. So we have different volunteers, some of them work with the voucher program, some work with the cat tracker, the adoption program. So depending on what you do with the rescue you may be in one or more of these groups. And when you log in to admin you only see the things that make sense to you. This is convenient and it works when you say, when you can do that, when you can say this volunteer is working with vouchers, they should get the altered track program, permission to do the vouchers and the altered track. It doesn't work when you need to give users access to a model but not necessarily all instances of a model or all fields of the model. It is possible also to use admin to customize down to that level. And what you can do there is nowadays with Django 1.7 I think it's pretty complete. Pretty much everything I've talked about here as you can set this option on your model admin to a sequence of whatever. There are methods on the model admin where you can implement a method to say get field sets, get list display, get fields. And those methods are given the request objects. So those methods have access to the user making the request. They can use the user to figure out this user should have access to these things in the database. So I should show them this list. This user should have access to these fields. So you can get fine grained per object permissions in the admin by overriding these model admin objects. And as an example, I've done this in a site where I wanted to allow the site administrators access to users, but I didn't want to allow the site administrators access to the super user attribute of a user. So I made super users completely invisible to them and the existence of that field completely invisible to them. So that you can do that as well. Okay. When do you start hitting limits? Some of the things that I have found lacking in admin, there's no reporting and I've been asked for it in many different ways, many different sites. You know, I'd really like to download a CSV of this thing. You can add it, there's no option to add it. And you know, there's no easy turn on. You can do it yourself. I've done it in a couple of different projects. I added reporting to the CapTracker database in a couple of different ways. So you can add it on, but it's not anything built into admin. There's also no workflow, no dashboard. We had one project where it was open to the end users and it had this complicated relationship of models and they wanted to say, the user needs to do this and then they need to create these things and they need to do this thing and then they need to do this and we need to do it in admin. That's not the way to admin. It doesn't provide a way to say, you know, when you're done with this, you go and do that. So it doesn't really provide any tips for doing that. There's no dashboard. When I use it for the CapTracker and I see other people use it for the CapTracker, I feel a lack of some notification kind of things to say, you know, you're the person that does health for the cats and you should know, you log in today, there's this list of cats that you're gonna need vaccination soon. I can build that as, you know, there's a report for vaccinations as to what's on coming soon. I can add in an email that sends the user but it'd be kind of nice if it was built into the site and admin doesn't have it and there's no easy way that I can think of to really add it in a general purpose way to admin. So when you need these kinds of things is when I start to think you grow or you're growing beyond the admin and you kind of wanna start doing your own thing. I just wanted to point out there's also the inlines are restricted to one level so you can edit things that are directly related to the object you're editing but not things that are related to that and there's a very old ticket in Django below 10,000 that requests nested inlines, you know, the ability to edit things related to the things related to the thing and a couple of people have made serious attempts to try to implement that. It hasn't happened yet, maybe it will someday. Ultimately though, another issue with building off the admin it doesn't look like your site. I've worked on a couple of sites where we have due to budget constraints and the needs. You know, here's some things you can do in admin and here's some things that we built on the front end of the site and they look pretty different. You can scan the admin, you can do a lot of work there but ultimately it looks like admin. The URLs look like admin. So there's some confusion that can result some of them. Why can we do these things in this part of the site and we can customize it really nicely and do exactly what we want and over here we can't? Well, because we're building off of admin over here so that can get to be an issue. So when you start hitting these things is when I start to say maybe we ought not be building off admin. And what do we do instead? And this is the part of the talk that I'm already almost out of time. So here's the second part of my talk which is condensed into one slide is you write your own code and Django actually makes this pretty easy. That's why we all love Django. If you have done a lot of admin customization you've probably wound up building a bunch of model forms already and you're probably gonna reuse those in building your custom whatever you need to go beyond admin. There are CSS frameworks and form helpers. I mean when you step outside the admin what you lose is the default reasonable looking presentation of your stuff. So you need to build your own CSS, your own form presentations and there's helpers to help do that or you can just wing it and do it yourself. If there's something that admin does that you really like you can always look at the admin code and see how it did it and pull it out and do it yourself. I did that with the crossword database when I was just getting started. I loved the filters. I wanted to do that in my own code. I looked to see how admin did it and I re-implemented it in my code. That was before they made it as easy as they did now. So you can also just crib from what's in admin. So that is what you do when you go beyond admin and I didn't run over yet and I had to have a picture of a cat and so I guess we have a little bit of time for questions.