models.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408
  1. """
  2. XX. Generating HTML forms from models
  3. This is mostly just a reworking of the ``form_for_model``/``form_for_instance``
  4. tests to use ``ModelForm``. As such, the text may not make sense in all cases,
  5. and the examples are probably a poor fit for the ``ModelForm`` syntax. In other
  6. words, most of these tests should be rewritten.
  7. """
  8. from __future__ import unicode_literals
  9. import datetime
  10. import os
  11. import tempfile
  12. from django.core import validators
  13. from django.core.exceptions import ValidationError
  14. from django.core.files.storage import FileSystemStorage
  15. from django.db import models
  16. from django.utils import six
  17. from django.utils.encoding import python_2_unicode_compatible
  18. from django.utils._os import upath
  19. temp_storage_dir = tempfile.mkdtemp(dir=os.environ['DJANGO_TEST_TEMP_DIR'])
  20. temp_storage = FileSystemStorage(temp_storage_dir)
  21. ARTICLE_STATUS = (
  22. (1, 'Draft'),
  23. (2, 'Pending'),
  24. (3, 'Live'),
  25. )
  26. ARTICLE_STATUS_CHAR = (
  27. ('d', 'Draft'),
  28. ('p', 'Pending'),
  29. ('l', 'Live'),
  30. )
  31. class Person(models.Model):
  32. name = models.CharField(max_length=100)
  33. @python_2_unicode_compatible
  34. class Category(models.Model):
  35. name = models.CharField(max_length=20)
  36. slug = models.SlugField(max_length=20)
  37. url = models.CharField('The URL', max_length=40)
  38. def __str__(self):
  39. return self.name
  40. def __repr__(self):
  41. return self.__str__()
  42. @python_2_unicode_compatible
  43. class Writer(models.Model):
  44. name = models.CharField(max_length=50, help_text='Use both first and last names.')
  45. class Meta:
  46. ordering = ('name',)
  47. def __str__(self):
  48. return self.name
  49. @python_2_unicode_compatible
  50. class Article(models.Model):
  51. headline = models.CharField(max_length=50)
  52. slug = models.SlugField()
  53. pub_date = models.DateField()
  54. created = models.DateField(editable=False)
  55. writer = models.ForeignKey(Writer)
  56. article = models.TextField()
  57. categories = models.ManyToManyField(Category, blank=True)
  58. status = models.PositiveIntegerField(choices=ARTICLE_STATUS, blank=True, null=True)
  59. def save(self, *args, **kwargs):
  60. if not self.id:
  61. self.created = datetime.date.today()
  62. return super(Article, self).save(*args, **kwargs)
  63. def __str__(self):
  64. return self.headline
  65. class ImprovedArticle(models.Model):
  66. article = models.OneToOneField(Article)
  67. class ImprovedArticleWithParentLink(models.Model):
  68. article = models.OneToOneField(Article, parent_link=True)
  69. class BetterWriter(Writer):
  70. score = models.IntegerField()
  71. @python_2_unicode_compatible
  72. class Publication(models.Model):
  73. title = models.CharField(max_length=30)
  74. date_published = models.DateField()
  75. def __str__(self):
  76. return self.title
  77. class Author(models.Model):
  78. publication = models.OneToOneField(Publication, null=True, blank=True)
  79. full_name = models.CharField(max_length=255)
  80. class Author1(models.Model):
  81. publication = models.OneToOneField(Publication, null=False)
  82. full_name = models.CharField(max_length=255)
  83. @python_2_unicode_compatible
  84. class WriterProfile(models.Model):
  85. writer = models.OneToOneField(Writer, primary_key=True)
  86. age = models.PositiveIntegerField()
  87. def __str__(self):
  88. return "%s is %s" % (self.writer, self.age)
  89. class Document(models.Model):
  90. myfile = models.FileField(upload_to='unused', blank=True)
  91. @python_2_unicode_compatible
  92. class TextFile(models.Model):
  93. description = models.CharField(max_length=20)
  94. file = models.FileField(storage=temp_storage, upload_to='tests', max_length=15)
  95. def __str__(self):
  96. return self.description
  97. class CustomFileField(models.FileField):
  98. def save_form_data(self, instance, data):
  99. been_here = getattr(self, 'been_saved', False)
  100. assert not been_here, "save_form_data called more than once"
  101. setattr(self, 'been_saved', True)
  102. class CustomFF(models.Model):
  103. f = CustomFileField(upload_to='unused', blank=True)
  104. class FilePathModel(models.Model):
  105. path = models.FilePathField(path=os.path.dirname(upath(__file__)), match=".*\.py$", blank=True)
  106. try:
  107. from PIL import Image # NOQA: detect if Pillow is installed
  108. test_images = True
  109. @python_2_unicode_compatible
  110. class ImageFile(models.Model):
  111. def custom_upload_path(self, filename):
  112. path = self.path or 'tests'
  113. return '%s/%s' % (path, filename)
  114. description = models.CharField(max_length=20)
  115. # Deliberately put the image field *after* the width/height fields to
  116. # trigger the bug in #10404 with width/height not getting assigned.
  117. width = models.IntegerField(editable=False)
  118. height = models.IntegerField(editable=False)
  119. image = models.ImageField(storage=temp_storage, upload_to=custom_upload_path,
  120. width_field='width', height_field='height')
  121. path = models.CharField(max_length=16, blank=True, default='')
  122. def __str__(self):
  123. return self.description
  124. @python_2_unicode_compatible
  125. class OptionalImageFile(models.Model):
  126. def custom_upload_path(self, filename):
  127. path = self.path or 'tests'
  128. return '%s/%s' % (path, filename)
  129. description = models.CharField(max_length=20)
  130. image = models.ImageField(storage=temp_storage, upload_to=custom_upload_path,
  131. width_field='width', height_field='height',
  132. blank=True, null=True)
  133. width = models.IntegerField(editable=False, null=True)
  134. height = models.IntegerField(editable=False, null=True)
  135. path = models.CharField(max_length=16, blank=True, default='')
  136. def __str__(self):
  137. return self.description
  138. except ImportError:
  139. test_images = False
  140. @python_2_unicode_compatible
  141. class CommaSeparatedInteger(models.Model):
  142. field = models.CommaSeparatedIntegerField(max_length=20)
  143. def __str__(self):
  144. return self.field
  145. class Homepage(models.Model):
  146. url = models.URLField()
  147. @python_2_unicode_compatible
  148. class Product(models.Model):
  149. slug = models.SlugField(unique=True)
  150. def __str__(self):
  151. return self.slug
  152. @python_2_unicode_compatible
  153. class Price(models.Model):
  154. price = models.DecimalField(max_digits=10, decimal_places=2)
  155. quantity = models.PositiveIntegerField()
  156. def __str__(self):
  157. return "%s for %s" % (self.quantity, self.price)
  158. class Meta:
  159. unique_together = (('price', 'quantity'),)
  160. class Triple(models.Model):
  161. left = models.IntegerField()
  162. middle = models.IntegerField()
  163. right = models.IntegerField()
  164. class Meta:
  165. unique_together = (('left', 'middle'), ('middle', 'right'))
  166. class ArticleStatus(models.Model):
  167. status = models.CharField(max_length=2, choices=ARTICLE_STATUS_CHAR, blank=True, null=True)
  168. @python_2_unicode_compatible
  169. class Inventory(models.Model):
  170. barcode = models.PositiveIntegerField(unique=True)
  171. parent = models.ForeignKey('self', to_field='barcode', blank=True, null=True)
  172. name = models.CharField(blank=False, max_length=20)
  173. class Meta:
  174. ordering = ('name',)
  175. def __str__(self):
  176. return self.name
  177. def __repr__(self):
  178. return self.__str__()
  179. class Book(models.Model):
  180. title = models.CharField(max_length=40)
  181. author = models.ForeignKey(Writer, blank=True, null=True)
  182. special_id = models.IntegerField(blank=True, null=True, unique=True)
  183. class Meta:
  184. unique_together = ('title', 'author')
  185. class BookXtra(models.Model):
  186. isbn = models.CharField(max_length=16, unique=True)
  187. suffix1 = models.IntegerField(blank=True, default=0)
  188. suffix2 = models.IntegerField(blank=True, default=0)
  189. class Meta:
  190. unique_together = (('suffix1', 'suffix2'))
  191. abstract = True
  192. class DerivedBook(Book, BookXtra):
  193. pass
  194. @python_2_unicode_compatible
  195. class ExplicitPK(models.Model):
  196. key = models.CharField(max_length=20, primary_key=True)
  197. desc = models.CharField(max_length=20, blank=True, unique=True)
  198. class Meta:
  199. unique_together = ('key', 'desc')
  200. def __str__(self):
  201. return self.key
  202. @python_2_unicode_compatible
  203. class Post(models.Model):
  204. title = models.CharField(max_length=50, unique_for_date='posted', blank=True)
  205. slug = models.CharField(max_length=50, unique_for_year='posted', blank=True)
  206. subtitle = models.CharField(max_length=50, unique_for_month='posted', blank=True)
  207. posted = models.DateField()
  208. def __str__(self):
  209. return self.title
  210. @python_2_unicode_compatible
  211. class DateTimePost(models.Model):
  212. title = models.CharField(max_length=50, unique_for_date='posted', blank=True)
  213. slug = models.CharField(max_length=50, unique_for_year='posted', blank=True)
  214. subtitle = models.CharField(max_length=50, unique_for_month='posted', blank=True)
  215. posted = models.DateTimeField(editable=False)
  216. def __str__(self):
  217. return self.title
  218. class DerivedPost(Post):
  219. pass
  220. @python_2_unicode_compatible
  221. class BigInt(models.Model):
  222. biggie = models.BigIntegerField()
  223. def __str__(self):
  224. return six.text_type(self.biggie)
  225. class MarkupField(models.CharField):
  226. def __init__(self, *args, **kwargs):
  227. kwargs["max_length"] = 20
  228. super(MarkupField, self).__init__(*args, **kwargs)
  229. def formfield(self, **kwargs):
  230. # don't allow this field to be used in form (real use-case might be
  231. # that you know the markup will always be X, but it is among an app
  232. # that allows the user to say it could be something else)
  233. # regressed at r10062
  234. return None
  235. class CustomFieldForExclusionModel(models.Model):
  236. name = models.CharField(max_length=10)
  237. markup = MarkupField()
  238. class FlexibleDatePost(models.Model):
  239. title = models.CharField(max_length=50, unique_for_date='posted', blank=True)
  240. slug = models.CharField(max_length=50, unique_for_year='posted', blank=True)
  241. subtitle = models.CharField(max_length=50, unique_for_month='posted', blank=True)
  242. posted = models.DateField(blank=True, null=True)
  243. @python_2_unicode_compatible
  244. class Colour(models.Model):
  245. name = models.CharField(max_length=50)
  246. def __iter__(self):
  247. for number in xrange(5):
  248. yield number
  249. def __str__(self):
  250. return self.name
  251. class ColourfulItem(models.Model):
  252. name = models.CharField(max_length=50)
  253. colours = models.ManyToManyField(Colour)
  254. class ArticleStatusNote(models.Model):
  255. name = models.CharField(max_length=20)
  256. status = models.ManyToManyField(ArticleStatus)
  257. class CustomErrorMessage(models.Model):
  258. name1 = models.CharField(max_length=50,
  259. validators=[validators.validate_slug],
  260. error_messages={'invalid': 'Model custom error message.'})
  261. name2 = models.CharField(max_length=50,
  262. validators=[validators.validate_slug],
  263. error_messages={'invalid': 'Model custom error message.'})
  264. def clean(self):
  265. if self.name1 == 'FORBIDDEN_VALUE':
  266. raise ValidationError({'name1': [ValidationError('Model.clean() error messages.')]})
  267. elif self.name1 == 'GLOBAL_ERROR':
  268. raise ValidationError("Global error message.")
  269. def today_callable_dict():
  270. return {"last_action__gte": datetime.datetime.today()}
  271. def today_callable_q():
  272. return models.Q(last_action__gte=datetime.datetime.today())
  273. class Character(models.Model):
  274. username = models.CharField(max_length=100)
  275. last_action = models.DateTimeField()
  276. class StumpJoke(models.Model):
  277. most_recently_fooled = models.ForeignKey(Character, limit_choices_to=today_callable_dict, related_name="+")
  278. has_fooled_today = models.ManyToManyField(Character, limit_choices_to=today_callable_q, related_name="+")