Skip to content

Django Models

[[Django Admin]]

https://betterprogramming.pub/everything-you-need-to-know-about-django-models-in-python-2a44ed4293dd

__str__ : used in template - ???? template?

Choices (Django 3)

Validators

Querying models (Django ORM)

Creating model fields

null=True vs blank=True

  • blank=True can leave it blank for forms
    • example: Django admin form
  • null=True the value in the table can be NULL

Foreign Keys

Set a foreign key to be something other than an ID

class DashboardWidget(models.Model):
    query = models.ForeignKey(
        DashboardQuery,  # the foreign model
        to_field="file_path",  # DashboardQuery.file_path, must be unique
        db_column="query_file_path",  # name of the db column, default: query_id (field-name_id)
        related_name=""
        ...
    )

ForeignKey.related_name

What is related_name used for?

from django.db import models

class Author(models.Model):
    name = models.CharField(max_length=200)

class Book(models.Model):
    author = models.ForeignKey(Author, on_delete=models.CASCADE)
    title = models.CharField(max_length=200)
>>> alex = Author.objects.get(name="Alex Smith")
>>> alex_books = Book.objects.filter(author=alex)

[!Equivalent using related_name]-

>>> alex = Author.objects.get(name="Alex Smith")
>>> alex_books = alex.book_set.all()

[!What would it look like if related_name=books]-

alex_books = alex.
- book_set.all()
+ books.all()

Doing a migration?

  • you need to drop the DashboardWidget.query and then re-create it
  • AlterField doesn't change the column type from int to text in Django 2.2

Enums in Models

You can also create a subclass for more precision Model Enum Types

class Dashboard(models.Model):
    class Status(models.TextChoices):
        ACTIVE = "active"
        DRAFT = "draft"

    status = models.CharField(
        max_length=6,
        choices=Status.choices,
        default=Status.DRAFT
    )
>>> Dashboard.Status.DRAFT
<Status.DRAFT: 'draft'>

Shorthand

MedalType = models.TextChoices('MedalType', 'GOLD SILVER BRONZE')
>>> MedalType.choices
[('GOLD', 'Gold'), ('SILVER', 'Silver'), ('BRONZE', 'Bronze')]

status = models.CharField(
        max_length=6,
        choices=Status.choices,
        default=Status.DRAFT
    )

Validating objects

Django docs: Validating objects

.clean() ????

Model properties

Django-Styleguide#properties

  • simple derived
from django.utils import timezone
from django.core.exceptions import ValidationError

class Course(BaseModel):
    name = models.CharField(unique=True, max_length=255)

    start_date = models.DateField()
    end_date = models.DateField()

    def clean(self):
        if self.start_date >= self.end_date:
            raise ValidationError("End date cannot be before start date")

    @property
    def has_started(self) -> bool:
        now = timezone.now()

        return self.start_date <= now.date()

    @property
    def has_finished(self) -> bool:
        now = timezone.now()

        return self.end_date <= now.date()

    # method
    def is_within(self, x: date) -> bool:
        return self.start_date <= x <= self.end_date

Migrations

Create an empty migration

useful when you're creating a custom migration

python manage.py makemigrations app_name --name migration_name --empty

Create a migration

operations list

from django.db import migrations

class Migration(migrations.Migration):

    dependencies = [
        ('dashboard_etl_manager', '0031_previous'),
    ]

    operations = [
        migrations.RunPython(python_function_name, migrations.RunPython.noop),
        migrations.RunSQL("SELECT * FROM table", migrations.RunSQL.noop),
    ]

Reverse a migration

operations = [
        migrations.RunSQL(
            'CREATE INDEX "app_sale_sold_at_b9438ae4" '
            'ON "app_sale" ("sold_at");',

            reverse_sql='DROP INDEX "app_sale_sold_at_b9438ae4";',
        ),
    ]

Then `python manage.py migrate app_name 0008

Add an index to a table that already has a ton of data in it


Last update: 2023-04-24