15774d694a28bf4f73f1fcdd20a94ccccd2ec688
10 Django Model Field Tricks You Probably Didn\342\200\231t Know About.md
| ... | ... | @@ -1,183 +0,0 @@ |
| 1 | -# 10 Django Model Field Tricks You Probably Didn’t Know About | by Haider Alaamery | Dec, 2024 | Medium |
|
| 2 | - |
|
| 3 | -https://halaamery.medium.com/10-django-model-field-tricks-you-probably-didnt-know-about-fc97f3148bc0 |
|
| 4 | - |
|
| 5 | - |
|
| 6 | -[Haider Alaamery](https://halaamery.medium.com/) |
|
| 7 | - |
|
| 8 | -1\. Customizing Field Validation with `clean()` |
|
| 9 | ------------------------------------------------ |
|
| 10 | - |
|
| 11 | -Did you know you can add custom validation logic to specific fields using the `clean()` method? |
|
| 12 | - |
|
| 13 | -``` |
|
| 14 | -from django.db import models |
|
| 15 | -from django.core.exceptions import ValidationError |
|
| 16 | -class Product(models.Model): |
|
| 17 | - name = models.CharField(max_length=100) |
|
| 18 | - price = models.DecimalField(max_digits=10, decimal_places=2) |
|
| 19 | -def clean(self): |
|
| 20 | - if self.price <= 0: |
|
| 21 | - raise ValidationError("Price must be greater than zero.") |
|
| 22 | -``` |
|
| 23 | - |
|
| 24 | - |
|
| 25 | -> Use `clean()` in your save logic or forms to ensure that invalid data never sneaks into your database. |
|
| 26 | - |
|
| 27 | -2\. Conditional Default Values |
|
| 28 | ------------------------------- |
|
| 29 | - |
|
| 30 | -Sometimes, you need default values to change dynamically based on some condition. |
|
| 31 | - |
|
| 32 | -``` |
|
| 33 | -from django.utils.timezone import now |
|
| 34 | -class Event(models.Model): |
|
| 35 | - name = models.CharField(max_length=100) |
|
| 36 | - start_date = models.DateTimeField(default=now) |
|
| 37 | - end_date = models.DateTimeField(default=lambda: now() + timedelta(days=7)) |
|
| 38 | -``` |
|
| 39 | - |
|
| 40 | - |
|
| 41 | -> This is great for timestamps or automatically generating default future dates. |
|
| 42 | - |
|
| 43 | -3\. **Unique Constraints for Multi-Field Uniqueness** |
|
| 44 | ------------------------------------------------------ |
|
| 45 | - |
|
| 46 | -Replace the deprecated unique\_together with UniqueConstraint for better control. |
|
| 47 | - |
|
| 48 | -``` |
|
| 49 | -from django.db import models |
|
| 50 | -class Membership(models.Model): |
|
| 51 | - user = models.ForeignKey('auth.User', on_delete=models.CASCADE) |
|
| 52 | - group = models.ForeignKey('Group', on_delete=models.CASCADE) |
|
| 53 | - class Meta: |
|
| 54 | - constraints = [ |
|
| 55 | - models.UniqueConstraint(fields=['user', 'group'], name='unique_membership') |
|
| 56 | - ] |
|
| 57 | -``` |
|
| 58 | - |
|
| 59 | - |
|
| 60 | -> This ensures no user can be added to the same group twice. |
|
| 61 | - |
|
| 62 | -4\. **Using** `**choices**` **with Enums** |
|
| 63 | ------------------------------------------- |
|
| 64 | - |
|
| 65 | -Django’s `choices` make fields cleaner, especially with Enums. |
|
| 66 | - |
|
| 67 | -``` |
|
| 68 | -from django.db import models |
|
| 69 | -class Status(models.TextChoices): |
|
| 70 | - PENDING = 'P', 'Pending' |
|
| 71 | - APPROVED = 'A', 'Approved' |
|
| 72 | - REJECTED = 'R', 'Rejected' |
|
| 73 | -class Request(models.Model): |
|
| 74 | - status = models.CharField(max_length=1, choices=Status.choices, default=Status.PENDING) |
|
| 75 | -``` |
|
| 76 | - |
|
| 77 | - |
|
| 78 | -> Access your choices like `Status.PENDING` instead of raw strings. |
|
| 79 | - |
|
| 80 | -5\. `through` for Many-to-Many Customization |
|
| 81 | --------------------------------------------- |
|
| 82 | - |
|
| 83 | -Need extra data in your Many-to-Many relationships? Use `through`. |
|
| 84 | - |
|
| 85 | -``` |
|
| 86 | -class Author(models.Model): |
|
| 87 | - name = models.CharField(max_length=100) |
|
| 88 | -class Book(models.Model): |
|
| 89 | - title = models.CharField(max_length=100) |
|
| 90 | - authors = models.ManyToManyField(Author, through='Authorship') |
|
| 91 | -class Authorship(models.Model): |
|
| 92 | - author = models.ForeignKey(Author, on_delete=models.CASCADE) |
|
| 93 | - book = models.ForeignKey(Book, on_delete=models.CASCADE) |
|
| 94 | - role = models.CharField(max_length=50) # e.g., "Co-Author", "Editor" |
|
| 95 | -``` |
|
| 96 | - |
|
| 97 | - |
|
| 98 | -> This allows you to track additional details about the relationship. |
|
| 99 | - |
|
| 100 | -6\. Field-Level Permissions with `editable=False` |
|
| 101 | -------------------------------------------------- |
|
| 102 | - |
|
| 103 | -Prevent direct editing in admin while still allowing updates programmatically. |
|
| 104 | - |
|
| 105 | -``` |
|
| 106 | -class Order(models.Model): |
|
| 107 | - total_price = models.DecimalField(max_digits=10, decimal_places=2, editable=False) |
|
| 108 | -``` |
|
| 109 | - |
|
| 110 | - |
|
| 111 | -> You can still update `total_price` in your code but not in the admin. |
|
| 112 | - |
|
| 113 | -7\. Custom File Upload Paths |
|
| 114 | ----------------------------- |
|
| 115 | - |
|
| 116 | -Organize uploaded files dynamically with `upload_to`. |
|
| 117 | - |
|
| 118 | -``` |
|
| 119 | -def upload_to(instance, filename): |
|
| 120 | - return f"uploads/{instance.user.id}/{filename}" |
|
| 121 | -class Profile(models.Model): |
|
| 122 | - user = models.OneToOneField('auth.User', on_delete=models.CASCADE) |
|
| 123 | - avatar = models.ImageField(upload_to=upload_to) |
|
| 124 | -``` |
|
| 125 | - |
|
| 126 | - |
|
| 127 | -> Uploaded files are neatly stored based on the user’s ID. |
|
| 128 | - |
|
| 129 | -8\. Using `Property` Fields in Models |
|
| 130 | -------------------------------------- |
|
| 131 | - |
|
| 132 | -Django models can include computed properties. |
|
| 133 | - |
|
| 134 | -``` |
|
| 135 | -class Employee(models.Model): |
|
| 136 | - first_name = models.CharField(max_length=50) |
|
| 137 | - last_name = models.CharField(max_length=50) |
|
| 138 | - @property |
|
| 139 | - def full_name(self): |
|
| 140 | - return f"{self.first_name} {self.last_name}" |
|
| 141 | -``` |
|
| 142 | - |
|
| 143 | - |
|
| 144 | -> Access it like a field: `employee.full_name`. |
|
| 145 | - |
|
| 146 | -9\. Auto-Setting Fields with `save()` |
|
| 147 | -------------------------------------- |
|
| 148 | - |
|
| 149 | -Customize field behavior by overriding `save()`. |
|
| 150 | - |
|
| 151 | -``` |
|
| 152 | -class Article(models.Model): |
|
| 153 | - title = models.CharField(max_length=100) |
|
| 154 | - slug = models.SlugField(unique=True, blank=True) |
|
| 155 | - def save(self, *args, **kwargs): |
|
| 156 | - if not self.slug: |
|
| 157 | - self.slug = self.title.lower().replace(' ', '-') |
|
| 158 | - super().save(*args, **kwargs) |
|
| 159 | -``` |
|
| 160 | - |
|
| 161 | - |
|
| 162 | -> Automatically generate slugs from titles if not provided. |
|
| 163 | - |
|
| 164 | -10\. Soft Deletes with `is_archived` |
|
| 165 | ------------------------------------- |
|
| 166 | - |
|
| 167 | -Instead of deleting records, archive them. |
|
| 168 | - |
|
| 169 | -``` |
|
| 170 | -class BaseModel(models.Model): |
|
| 171 | - is_archived = models.BooleanField(default=False) |
|
| 172 | - def delete(self, *args, **kwargs): |
|
| 173 | - self.is_archived = True |
|
| 174 | - self.save() |
|
| 175 | - class Meta: |
|
| 176 | - abstract = True |
|
| 177 | -``` |
|
| 178 | - |
|
| 179 | - |
|
| 180 | -> Your data stays safe while appearing “deleted” to the user. |
|
| 181 | - |
|
| 182 | -**Conclusion:** |
|
| 183 | -Django’s model system is powerful, and these tricks can help you write cleaner, more efficient code. Which of these tricks are you already using? What’s your favorite? |
|
| ... | ... | \ No newline at end of file |