defines how many will be displayed per page, and if there are more than that, \u201cNext\u201d and \u201cPrevious\u201d links will be displayed to let you navigate through the pages. ordering simply defines the order in which the items are displayed, and is set up the same way the ordering variable was in our model classes. Your products and categories listings will be searchable in the backend. search_fields is a Python list of fields against which you want to match text. If we search our products for \u201cacoustic,\u201d any product with the word \u201cacoustic\u201d in its name, description, or meta tags will be listed for us. Also, each item will be given its own \u201cEdit\u201d page, where you\u2019ll be presented with a form where you can change the item\u2019s values. It makes little sense for us to display created_at and updated_at, since the user was never meant to edit those fields. Because of this, we list the two fields in a tuple that is assigned to a variable called exclude. With this in place, these date fields will not appear on the \u201cEdit\u201d form. There is also a prepopulated_fields variable for each admin model. We\u2019ll see how this works in just a moment when we get to our interface. Lastly, we register the model, and its corresponding admin model, with the admin interface by calling admin.site.register(Model, ModelAdmin) twice, once for each model. That\u2019s it! Setting up these classes and registering their associated models with the admin is very simple. www.it-ebooks.info CHAPTER 3 \u25a0 MODELS FOR SALE 59 So, there is one last bit of code in the ProductAdmin model that you might still be curious about, and that\u2019s the line reading form = ProductAdminForm. We\u2019ve put this in place because we need some validation on our models, which we\u2019re going to examine in the next section. WHAT ABOUT AN INTERNAL \u201cADMIN\u201d CLASS? You may have seen some code out in the wild that doesn\u2019t create a separate admin.py file to get your models hooked up to the admin interface. Instead, an internal class called Admin is created inside the model class, very similar to the internal Meta class, and it contains all of the information to customize your admin interface. This method was used in versions of Django prior to the 1.0 release. If you\u2019re working with an earlier version, you\u2019ll have to consult the online docs in order to determine how to hook up your models. Most of the syntax is the same\u2026 you just need to do that one thing differently. A Note on Model Validation One key part of setting up your data, and maintaining its integrity, is setting up validation on your models. For example, you don\u2019t want a category or product name to be blank, their slug fields must be different for every record, and you enforced a maximum length on many of your fields. If you try to add any category or product that violates these constraints, the add operation will fail. A lot of this was done implicitly, without us having to do anything out of the ordinary. This is more based on luck. Django\u2019s default settings on model fields just happened to suit our needs, and we overwrote them when there was an exception to the default rules. For the most part, this is acceptable; the default rules are good ones. Just make sure you\u2019re familiar with them, so you know what you need to change. If nothing else, at least remember that all fields cannot be empty unless you set blank=False. Sometimes, however, the default validation is not enough. Your admin interface will enforce a few rules, but your models might have some custom rules that you need enforced. One perfect example of this is your product price. It should never be zero or a negative number, but right now, users can enter whatever number they want. Django\u2019s models don\u2019t support validating number ranges just by specifying an argument in the field declaration in the class. So, we need to do a little extra work. Django allows you to hook up your own custom validation to its admin interface forms by declaring your own, and then associating the form with your admin model. Have a look at the admin.py code, and reread the three import statements. Notice the third one: from ecomstore.catalog.forms import ProductAdminForm If this is confusing to you, don\u2019t worry; it doesn\u2019t exist because we haven\u2019t created it yet. Go ahead and create another file in your catalog directory called forms.py, and add the following code to it: from django import forms from ecomstore.catalog.models import Product class ProductAdminForm(forms.ModelForm): class Meta: model = Product www.it-ebooks.info CHAPTER 3 \u25a0 MODELS FOR SALE 60 def clean_price(self): if self.cleaned_data['price'] <= 0: raise forms.ValidationError('Price must be greater than zero.') return self.cleaned_data['price'] This code bears a closer look on our part. We declare a new class called ProductAdminForm, which inherits from forms.ModelForm. You can call this class anything you like, but ProductAdminForm is a good name that makes its purpose clear to anyone reading the code. Inside, we declare an internal class called Meta, just like within our model classes, inside of which we tell our class which model it should be associated with. Then, we declare one method that actually performs the validation. These methods take the form of clean_[field name]. Because our field is called price, we create a method named clean_price. The methods prefaced by clean_ are run before the data is allowed to be saved into the database. We can access the value of price from a Python dictionary named cleaned_data, by looking it up by name. We check the value and, if it\u2019s less than or equal to zero, we raise a forms ValidationError with a message informing the user of the error. Of course, if no error is encountered, the error is not raised and the data is returned to Django for saving the model instance (in this case, one of our products). The admin form for the product is already set up for us, but we just needed to inject a little bit of extra code that enforces our one rule regarding the price. Syncing Up the Models Now, after all of that hard work, we\u2019re finally going to run our manage.py syncdb script again and create our product, category, and join tables. Before we do this, there are a couple other scripts available from manage.py that you might find quite useful. The first one is validate. Go ahead and run the following in your shell from the root of your project: $ python manage.py validate This will scan your models to make sure that the syntax you\u2019ve used in entering them is correct. If there is anything you missed, it will let you know something is wrong before you commit these models to your database. Think of it as spell check for your model code. If you\u2019ve entered everything correctly, you should get the following output: 0 errors found It won\u2019t catch everything, but it\u2019s a nice safety net. If you do get any errors, read the information it gives you and use that to fix any problems. The next one allows you to get a look at the Data Definition Language (DDL) output that Django will run on your database in order to create the tables. This is how Django talks to your database\u2026 it translates all of the Python code for your model classes into a language that the database will understand. This is much easier on you, since you would otherwise be creating this DDL by hand. In MySQL, this DDL takes the form of CREATE TABLE 'products', followed by a comma-delimited list in parentheses of all your fields and their data types. To view the DDL, run the following command: $ python manage.py sqlall catalog The word catalog you\u2019ve supplied is the name of the app containing the models you want the DDL for. You can request the DDL for more than one app, if you would like.