Fixtures are data collections that Django may read and load into its database. Fixtures store data that has already been collected. Fixtures are essentially a means for Django to export and import data into a database.
What is the best way to initialize data for models?
When you’re first putting up an app, it can be helpful to pre-populate your database with hard-coded data. With migrations or fixtures, you can offer initial data.
Providing initial data using migrations
Create a data migration if you wish to automatically load initial data for an app. When setting up the test database, migrations are performed, ensuring that the data is available there. However, it is subject to some limits.
Using Fixtures to provide initial data
You can alternatively give initial data via fixtures. However, unless you use TransactionTestCase.fixtures, this data isn’t loaded automatically. A fixture is a grouping of Django’s data to populate a database. The manage.py dumpdata command is the most straightforward approach to creating a fixture if you already have some data.
Supported Formats and data structures
Fixtures can also be written by hand and saved as JSON, XML, or YAML with PyYAML installed. Django anticipates a specific pattern in the fixtures. Any other pattern would result in an error. More information on each of the various serialization formats is found in the serialization documentation.
But, as an example, consider the following JSON fixture for a Student model:
[ { "model": "schoolapp.student ", "pk": 1, "fields": { "first_name": "Ann", "last_name": "Thompson" } }, { "model": "schoolapp.student", "pk": 2, "fields": { "first_name": "Tom", "last_name": "Keen" } } ]
The following is a similar fixture but as YAML:
- model: schoolapp.student pk: 1 fields: first_name: Ann last_name: Thompson - model: schoolapp.student pk: 2 fields: first_name: Tom last_name: Keen
The XML fixture differs from JSON and YML in that, in addition to being XML, it requires certain additional metadata. A version number with ‘django-objects’ and kind of value in the field, for example. In XML, the same data that we’ve just seen would look like this:
<?xml version="1.0" encoding="UTF-8"?> <django-objects version="1.0"> <object pk="1" model="schoolapp.student"> <field type="CharField" name="first_name">Ann</field> <field type="CharField" name="last_name">Thompson</field> </object> <object pk="2" model="schoolapp.student"> <field type="CharField" name="first_name">Tom</field> <field type="CharField" name="last_name">Keen</field> </object> </django-objects>
This information is saved in a fixtures directory within your app.
You can load data using the manage.py loaddata command, by providing the name of the fixture file. For the data to be re-loaded into the databases, each time you execute loaddata, it has to be read from the fixture. Note that if you edit one of the rows created by a fixture and then rerun loaddata, all changes you’ve made are wiped out.
Where does Django look for fixture files?
Django looks for fixtures in each app’s fixtures directory by default. The FIXTURE_DIRS parameter is used to specify a list of additional directories where Django should look.
Django can find fixtures in a project in three different places. These include:
Django searches for a fixtures directory inside an application by default. It would be the first place to look. We’ll need to establish a new directory called ‘fixtures’ inside the app to make this work. Then we can put our app-specific fixtures in that directory.
Project Scope: We can also save all of our fixtures at the project level. It would necessitate the creation of a ‘fixtures’ directory in the project root level. Then, in our settings.py file, we need to add the FIXTURE_DIRS settings to point to the places where the fixtures are stored. In addition to the app-scoped directories, Django will search the locations specified by this parameter. Below is a semblance of how it would look like:
FIXTURE_DIRS = [ 'fixtures', ]
It’s worth noting that the FIXTURE DIRS setting needs a list of locations.
Adding an argument to the command would run to load the data. You can also specify a path to a fixture file when running manage.py loaddata, which will search that directory instead of the standard directories – search for a specific location or file.
Consider the following scenario:
python manage.py loaddata path/to/your/data.json
The testing framework also makes use of fixtures to assist build up a consistent test environment.
Django Fixtures Maintenance
Django fixtures are fantastic, but they come with a few drawbacks:
Keeping fixtures up to date
Django fixtures must contain all of the model’s needed fields. You must update the fixtures if you introduce a new field that is not nullable. They will not load if this is not done. When you have a lot of Django fixtures, it might be challenging to keep them all up to date.
Maintaining fixture dependencies
Django fixtures that rely on other fixtures must be loaded in a specific order and jointly. Keeping track of fixtures is a massive task as new test cases are added and existing test cases are adjusted.
As a result, Django fixtures aren’t a good fit for models that change frequently. Maintaining Django fixtures for models that represent fundamental app objects like orders, transactions, sales, or reservations, for example, would be pretty tough.
Django fixtures, on the other hand, are an excellent choice for the following scenarios:
- Models that rarely change, such as country codes and zip codes, are some examples of constant data.
- Models that hold your app’s lookup data, such as product categories, user groups, and the types of users.
Commands
So far, we’ve primarily talked about importing data into databases. Still, we can also use the ‘export’ capability to produce fixtures automatically if we already have some data in the database. So, to deal with the fixtures, there are essentially two commands:
Commands when data is being loaded
The loaddata command is used to insert data into a database. We can accomplish this in several ways:
# show the file location, name, and extension python manage.py loaddata path/to/the/file/data.json # make known the file name and extension python manage.py loaddata data.json # indicates the file name python manage.py loaddata data
We feel this would require some explanation. We gave a pathname, filename, and extension in the first command. Django tries to find the given file in all previously defined ‘fixture’ directories.
In case of a ‘fixtures’ directory present at the projects’ root directory, Django will search for the directory structure (path/to/the/file/) and the filename within that structure with the correct extension using this command. Because there is no pathname in the second command, Django will seek the filename and extension in any supplied locations.
Django will seek any file with the supplied name in the specified locations in the third command; the extensions won’t matter in this situation. To be clear, if we specify the fixture’s extension, Django will first call the specific serializer to deserialize the data. Django looks for the file first and then calls the serializer based on the file’s extension if the extension isn’t specified. We can add the following parameters to our loaddata command:
–database
specifies the database used to load the data. The default database defined in the settings.py file will be used by default.
–app
specifies the app where the fixtures should be found.
–format
sets the serialization format (JSON, XML, or YAML).
–exclude
specifies any files that should not be loaded.
Data dumping
Fixtures are created using dumpdata. The command, we suppose, is self-explanatory. The dumpdata command is run with the model name scoped to the app’s label that contains the model. We’d also like to specify the format in which the data is delivered. There are a few other things we can do with this command:
–all or -a
Django’s default Model Manager is used to dump all data with –all or -a.
–indent
specifies the indent size in integer format.
–exclude
specifies which apps or models are included in the dump.
–database
specifies the database’s name
–pks [list of primary keys]
defines the primary keys to dump; only one model is supported.
–output
gives the name of the file used to dump the data.
Data to be used as a starting point for the entire project
Executing the command numerous times to seed data from multiple sources is inefficient. We can handle this by issuing a command that loads all fixtures while using wildcard characters. Inside the directory dubbed ‘fixtures’ where the loading of the JSON files happens, the command will look like this:
python manage.py loaddata fixtures/*.json
Conclusion
If you’re working with Django, fixtures can help you come up with easy-to-maintain tests for your models. Django has its method for creating and loading model fixtures from files. Fixture files in Django are authored in JSON, XML or YAML.
Fixtures are an essential part of making your test suite efficient and effective, and writing good tests is crucial in maintaining a successful app. Fixtures are small bits of information that serve as the starting point for your tests.
It can be a hassle to create, edit, and manage your fixtures when your test cases change, and that is an ideal case where fixtures come in handy. As we have examined, using an existing object is the most straightforward approach to make a Django fixture.