mirror of
				https://github.com/django/django.git
				synced 2025-10-31 09:41:08 +00:00 
			
		
		
		
	Ensured that rel_obj_attr and instance_attr return matching (pk, cls) tuples in GenericForeignKey.get_prefetch_queryset(), preventing mismatches when prefetching related objects where pk and get_prep_value() differ. Using value_to_string() also makes this code compatible with composite primary keys.
		
			
				
	
	
		
			326 lines
		
	
	
		
			8.4 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			326 lines
		
	
	
		
			8.4 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| import uuid
 | |
| 
 | |
| from django.contrib.contenttypes.fields import GenericForeignKey, GenericRelation
 | |
| from django.contrib.contenttypes.models import ContentType
 | |
| from django.db import models
 | |
| from django.db.models.query import ModelIterable
 | |
| from django.utils.functional import cached_property
 | |
| 
 | |
| 
 | |
| class Author(models.Model):
 | |
|     name = models.CharField(max_length=50, unique=True)
 | |
|     first_book = models.ForeignKey(
 | |
|         "Book", models.CASCADE, related_name="first_time_authors"
 | |
|     )
 | |
|     favorite_authors = models.ManyToManyField(
 | |
|         "self", through="FavoriteAuthors", symmetrical=False, related_name="favors_me"
 | |
|     )
 | |
| 
 | |
|     class Meta:
 | |
|         ordering = ["id"]
 | |
| 
 | |
|     def __str__(self):
 | |
|         return self.name
 | |
| 
 | |
| 
 | |
| class AuthorWithAge(Author):
 | |
|     author = models.OneToOneField(Author, models.CASCADE, parent_link=True)
 | |
|     age = models.IntegerField()
 | |
| 
 | |
| 
 | |
| class FavoriteAuthors(models.Model):
 | |
|     author = models.ForeignKey(
 | |
|         Author, models.CASCADE, to_field="name", related_name="i_like"
 | |
|     )
 | |
|     likes_author = models.ForeignKey(
 | |
|         Author, models.CASCADE, to_field="name", related_name="likes_me"
 | |
|     )
 | |
|     is_active = models.BooleanField(default=True)
 | |
| 
 | |
|     class Meta:
 | |
|         ordering = ["id"]
 | |
| 
 | |
| 
 | |
| class AuthorAddress(models.Model):
 | |
|     author = models.ForeignKey(
 | |
|         Author, models.CASCADE, to_field="name", related_name="addresses"
 | |
|     )
 | |
|     address = models.TextField()
 | |
| 
 | |
|     class Meta:
 | |
|         ordering = ["id"]
 | |
| 
 | |
| 
 | |
| class Book(models.Model):
 | |
|     title = models.CharField(max_length=255)
 | |
|     authors = models.ManyToManyField(Author, related_name="books")
 | |
| 
 | |
|     class Meta:
 | |
|         ordering = ["id"]
 | |
| 
 | |
| 
 | |
| class BookWithYear(Book):
 | |
|     book = models.OneToOneField(Book, models.CASCADE, parent_link=True)
 | |
|     published_year = models.IntegerField()
 | |
|     aged_authors = models.ManyToManyField(AuthorWithAge, related_name="books_with_year")
 | |
| 
 | |
| 
 | |
| class Bio(models.Model):
 | |
|     author = models.OneToOneField(
 | |
|         Author,
 | |
|         models.CASCADE,
 | |
|         primary_key=True,
 | |
|         to_field="name",
 | |
|     )
 | |
|     books = models.ManyToManyField(Book, blank=True)
 | |
| 
 | |
| 
 | |
| class Reader(models.Model):
 | |
|     name = models.CharField(max_length=50)
 | |
|     books_read = models.ManyToManyField(Book, related_name="read_by")
 | |
| 
 | |
|     class Meta:
 | |
|         ordering = ["id"]
 | |
| 
 | |
|     def __str__(self):
 | |
|         return self.name
 | |
| 
 | |
| 
 | |
| class BookReview(models.Model):
 | |
|     # Intentionally does not have a related name.
 | |
|     book = models.ForeignKey(BookWithYear, models.CASCADE, null=True)
 | |
|     notes = models.TextField(null=True, blank=True)
 | |
| 
 | |
| 
 | |
| # Models for default manager tests
 | |
| 
 | |
| 
 | |
| class Qualification(models.Model):
 | |
|     name = models.CharField(max_length=10)
 | |
| 
 | |
|     class Meta:
 | |
|         ordering = ["id"]
 | |
| 
 | |
| 
 | |
| class ModelIterableSubclass(ModelIterable):
 | |
|     pass
 | |
| 
 | |
| 
 | |
| class TeacherQuerySet(models.QuerySet):
 | |
|     def __init__(self, *args, **kwargs):
 | |
|         super().__init__(*args, **kwargs)
 | |
|         self._iterable_class = ModelIterableSubclass
 | |
| 
 | |
| 
 | |
| class TeacherManager(models.Manager):
 | |
|     def get_queryset(self):
 | |
|         return super().get_queryset().prefetch_related("qualifications")
 | |
| 
 | |
| 
 | |
| class Teacher(models.Model):
 | |
|     name = models.CharField(max_length=50)
 | |
|     qualifications = models.ManyToManyField(Qualification)
 | |
| 
 | |
|     objects = TeacherManager()
 | |
|     objects_custom = TeacherQuerySet.as_manager()
 | |
| 
 | |
|     class Meta:
 | |
|         ordering = ["id"]
 | |
| 
 | |
|     def __str__(self):
 | |
|         return "%s (%s)" % (
 | |
|             self.name,
 | |
|             ", ".join(q.name for q in self.qualifications.all()),
 | |
|         )
 | |
| 
 | |
| 
 | |
| class Department(models.Model):
 | |
|     name = models.CharField(max_length=50)
 | |
|     teachers = models.ManyToManyField(Teacher)
 | |
| 
 | |
|     class Meta:
 | |
|         ordering = ["id"]
 | |
| 
 | |
| 
 | |
| # GenericRelation/GenericForeignKey tests
 | |
| 
 | |
| 
 | |
| class TaggedItem(models.Model):
 | |
|     tag = models.SlugField()
 | |
|     content_type = models.ForeignKey(
 | |
|         ContentType,
 | |
|         models.CASCADE,
 | |
|         related_name="taggeditem_set2",
 | |
|     )
 | |
|     object_id = models.PositiveIntegerField()
 | |
|     content_object = GenericForeignKey("content_type", "object_id")
 | |
|     created_by_ct = models.ForeignKey(
 | |
|         ContentType,
 | |
|         models.SET_NULL,
 | |
|         null=True,
 | |
|         related_name="taggeditem_set3",
 | |
|     )
 | |
|     created_by_fkey = models.PositiveIntegerField(null=True)
 | |
|     created_by = GenericForeignKey(
 | |
|         "created_by_ct",
 | |
|         "created_by_fkey",
 | |
|     )
 | |
|     favorite_ct = models.ForeignKey(
 | |
|         ContentType,
 | |
|         models.SET_NULL,
 | |
|         null=True,
 | |
|         related_name="taggeditem_set4",
 | |
|     )
 | |
|     favorite_fkey = models.CharField(max_length=64, null=True)
 | |
|     favorite = GenericForeignKey("favorite_ct", "favorite_fkey")
 | |
| 
 | |
|     class Meta:
 | |
|         ordering = ["id"]
 | |
| 
 | |
| 
 | |
| class Article(models.Model):
 | |
|     id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
 | |
|     name = models.CharField(max_length=20)
 | |
| 
 | |
| 
 | |
| class Bookmark(models.Model):
 | |
|     url = models.URLField()
 | |
|     tags = GenericRelation(TaggedItem, related_query_name="bookmarks")
 | |
|     favorite_tags = GenericRelation(
 | |
|         TaggedItem,
 | |
|         content_type_field="favorite_ct",
 | |
|         object_id_field="favorite_fkey",
 | |
|         related_query_name="favorite_bookmarks",
 | |
|     )
 | |
| 
 | |
|     class Meta:
 | |
|         ordering = ["id"]
 | |
| 
 | |
| 
 | |
| class Comment(models.Model):
 | |
|     comment = models.TextField()
 | |
| 
 | |
|     # Content-object field
 | |
|     content_type = models.ForeignKey(ContentType, models.CASCADE, null=True)
 | |
|     object_pk = models.TextField()
 | |
|     content_object = GenericForeignKey(ct_field="content_type", fk_field="object_pk")
 | |
|     content_type_uuid = models.ForeignKey(
 | |
|         ContentType, models.CASCADE, related_name="comments", null=True
 | |
|     )
 | |
|     object_pk_uuid = models.TextField()
 | |
|     content_object_uuid = GenericForeignKey(
 | |
|         ct_field="content_type_uuid", fk_field="object_pk_uuid"
 | |
|     )
 | |
| 
 | |
|     class Meta:
 | |
|         ordering = ["id"]
 | |
| 
 | |
| 
 | |
| class ArticleCustomUUID(models.Model):
 | |
|     class CustomUUIDField(models.UUIDField):
 | |
|         def get_prep_value(self, value):
 | |
|             return str(value)
 | |
| 
 | |
|     id = CustomUUIDField(primary_key=True, default=uuid.uuid4)
 | |
|     name = models.CharField(max_length=30)
 | |
| 
 | |
| 
 | |
| # Models for lookup ordering tests
 | |
| 
 | |
| 
 | |
| class House(models.Model):
 | |
|     name = models.CharField(max_length=50)
 | |
|     address = models.CharField(max_length=255)
 | |
|     owner = models.ForeignKey("Person", models.SET_NULL, null=True)
 | |
|     main_room = models.OneToOneField(
 | |
|         "Room", models.SET_NULL, related_name="main_room_of", null=True
 | |
|     )
 | |
| 
 | |
|     class Meta:
 | |
|         ordering = ["id"]
 | |
| 
 | |
| 
 | |
| class Room(models.Model):
 | |
|     name = models.CharField(max_length=50)
 | |
|     house = models.ForeignKey(House, models.CASCADE, related_name="rooms")
 | |
| 
 | |
|     class Meta:
 | |
|         ordering = ["id"]
 | |
| 
 | |
| 
 | |
| class Person(models.Model):
 | |
|     name = models.CharField(max_length=50)
 | |
|     houses = models.ManyToManyField(House, related_name="occupants")
 | |
| 
 | |
|     @property
 | |
|     def primary_house(self):
 | |
|         # Assume business logic forces every person to have at least one house.
 | |
|         return sorted(self.houses.all(), key=lambda house: -house.rooms.count())[0]
 | |
| 
 | |
|     @property
 | |
|     def all_houses(self):
 | |
|         return list(self.houses.all())
 | |
| 
 | |
|     @cached_property
 | |
|     def cached_all_houses(self):
 | |
|         return self.all_houses
 | |
| 
 | |
|     class Meta:
 | |
|         ordering = ["id"]
 | |
| 
 | |
| 
 | |
| # Models for nullable FK tests
 | |
| 
 | |
| 
 | |
| class Employee(models.Model):
 | |
|     name = models.CharField(max_length=50)
 | |
|     boss = models.ForeignKey("self", models.SET_NULL, null=True, related_name="serfs")
 | |
| 
 | |
|     class Meta:
 | |
|         ordering = ["id"]
 | |
| 
 | |
| 
 | |
| # Ticket #19607
 | |
| 
 | |
| 
 | |
| class LessonEntry(models.Model):
 | |
|     name1 = models.CharField(max_length=200)
 | |
|     name2 = models.CharField(max_length=200)
 | |
| 
 | |
| 
 | |
| class WordEntry(models.Model):
 | |
|     lesson_entry = models.ForeignKey(LessonEntry, models.CASCADE)
 | |
|     name = models.CharField(max_length=200)
 | |
| 
 | |
| 
 | |
| # Ticket #21410: Regression when related_name="+"
 | |
| 
 | |
| 
 | |
| class Author2(models.Model):
 | |
|     name = models.CharField(max_length=50, unique=True)
 | |
|     first_book = models.ForeignKey(
 | |
|         "Book", models.CASCADE, related_name="first_time_authors+"
 | |
|     )
 | |
|     favorite_books = models.ManyToManyField("Book", related_name="+")
 | |
| 
 | |
|     class Meta:
 | |
|         ordering = ["id"]
 | |
| 
 | |
| 
 | |
| # Models for many-to-many with UUID pk test:
 | |
| 
 | |
| 
 | |
| class Pet(models.Model):
 | |
|     id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
 | |
|     name = models.CharField(max_length=20)
 | |
|     people = models.ManyToManyField(Person, related_name="pets")
 | |
| 
 | |
| 
 | |
| class Flea(models.Model):
 | |
|     id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
 | |
|     current_room = models.ForeignKey(
 | |
|         Room, models.SET_NULL, related_name="fleas", null=True
 | |
|     )
 | |
|     pets_visited = models.ManyToManyField(Pet, related_name="fleas_hosted")
 | |
|     people_visited = models.ManyToManyField(Person, related_name="fleas_hosted")
 |