models.py 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492
  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. import uuid
  13. from django.core import validators
  14. from django.core.exceptions import ValidationError
  15. from django.core.files.storage import FileSystemStorage
  16. from django.db import models
  17. from django.utils import six
  18. from django.utils._os import upath
  19. from django.utils.encoding import python_2_unicode_compatible
  20. from django.utils.six.moves import range
  21. temp_storage_dir = tempfile.mkdtemp()
  22. temp_storage = FileSystemStorage(temp_storage_dir)
  23. ARTICLE_STATUS = (
  24. (1, 'Draft'),
  25. (2, 'Pending'),
  26. (3, 'Live'),
  27. )
  28. ARTICLE_STATUS_CHAR = (
  29. ('d', 'Draft'),
  30. ('p', 'Pending'),
  31. ('l', 'Live'),
  32. )
  33. class Person(models.Model):
  34. name = models.CharField(max_length=100)
  35. @python_2_unicode_compatible
  36. class Category(models.Model):
  37. name = models.CharField(max_length=20)
  38. slug = models.SlugField(max_length=20)
  39. url = models.CharField('The URL', max_length=40)
  40. def __str__(self):
  41. return self.name
  42. def __repr__(self):
  43. return self.__str__()
  44. @python_2_unicode_compatible
  45. class Writer(models.Model):
  46. name = models.CharField(max_length=50, help_text='Use both first and last names.')
  47. class Meta:
  48. ordering = ('name',)
  49. def __str__(self):
  50. return self.name
  51. @python_2_unicode_compatible
  52. class Article(models.Model):
  53. headline = models.CharField(max_length=50)
  54. slug = models.SlugField()
  55. pub_date = models.DateField()
  56. created = models.DateField(editable=False)
  57. writer = models.ForeignKey(Writer, models.CASCADE)
  58. article = models.TextField()
  59. categories = models.ManyToManyField(Category, blank=True)
  60. status = models.PositiveIntegerField(choices=ARTICLE_STATUS, blank=True, null=True)
  61. def save(self, *args, **kwargs):
  62. if not self.id:
  63. self.created = datetime.date.today()
  64. return super(Article, self).save(*args, **kwargs)
  65. def __str__(self):
  66. return self.headline
  67. class ImprovedArticle(models.Model):
  68. article = models.OneToOneField(Article, models.CASCADE)
  69. class ImprovedArticleWithParentLink(models.Model):
  70. article = models.OneToOneField(Article, models.CASCADE, parent_link=True)
  71. class BetterWriter(Writer):
  72. score = models.IntegerField()
  73. @python_2_unicode_compatible
  74. class Publication(models.Model):
  75. title = models.CharField(max_length=30)
  76. date_published = models.DateField()
  77. def __str__(self):
  78. return self.title
  79. def default_mode():
  80. return 'di'
  81. def default_category():
  82. return 3
  83. class PublicationDefaults(models.Model):
  84. MODE_CHOICES = (('di', 'direct'), ('de', 'delayed'))
  85. CATEGORY_CHOICES = ((1, 'Games'), (2, 'Comics'), (3, 'Novel'))
  86. title = models.CharField(max_length=30)
  87. date_published = models.DateField(default=datetime.date.today)
  88. datetime_published = models.DateTimeField(default=datetime.datetime(2000, 1, 1))
  89. mode = models.CharField(max_length=2, choices=MODE_CHOICES, default=default_mode)
  90. category = models.IntegerField(choices=CATEGORY_CHOICES, default=default_category)
  91. active = models.BooleanField(default=True)
  92. file = models.FileField(default='default.txt')
  93. class Author(models.Model):
  94. publication = models.OneToOneField(Publication, models.SET_NULL, null=True, blank=True)
  95. full_name = models.CharField(max_length=255)
  96. class Author1(models.Model):
  97. publication = models.OneToOneField(Publication, models.CASCADE, null=False)
  98. full_name = models.CharField(max_length=255)
  99. @python_2_unicode_compatible
  100. class WriterProfile(models.Model):
  101. writer = models.OneToOneField(Writer, models.CASCADE, primary_key=True)
  102. age = models.PositiveIntegerField()
  103. def __str__(self):
  104. return "%s is %s" % (self.writer, self.age)
  105. class Document(models.Model):
  106. myfile = models.FileField(upload_to='unused', blank=True)
  107. @python_2_unicode_compatible
  108. class TextFile(models.Model):
  109. description = models.CharField(max_length=20)
  110. file = models.FileField(storage=temp_storage, upload_to='tests', max_length=15)
  111. def __str__(self):
  112. return self.description
  113. class CustomFileField(models.FileField):
  114. def save_form_data(self, instance, data):
  115. been_here = getattr(self, 'been_saved', False)
  116. assert not been_here, "save_form_data called more than once"
  117. setattr(self, 'been_saved', True)
  118. class CustomFF(models.Model):
  119. f = CustomFileField(upload_to='unused', blank=True)
  120. class FilePathModel(models.Model):
  121. path = models.FilePathField(path=os.path.dirname(upath(__file__)), match=r".*\.py$", blank=True)
  122. try:
  123. from PIL import Image # NOQA: detect if Pillow is installed
  124. test_images = True
  125. @python_2_unicode_compatible
  126. class ImageFile(models.Model):
  127. def custom_upload_path(self, filename):
  128. path = self.path or 'tests'
  129. return '%s/%s' % (path, filename)
  130. description = models.CharField(max_length=20)
  131. # Deliberately put the image field *after* the width/height fields to
  132. # trigger the bug in #10404 with width/height not getting assigned.
  133. width = models.IntegerField(editable=False)
  134. height = models.IntegerField(editable=False)
  135. image = models.ImageField(storage=temp_storage, upload_to=custom_upload_path,
  136. width_field='width', height_field='height')
  137. path = models.CharField(max_length=16, blank=True, default='')
  138. def __str__(self):
  139. return self.description
  140. @python_2_unicode_compatible
  141. class OptionalImageFile(models.Model):
  142. def custom_upload_path(self, filename):
  143. path = self.path or 'tests'
  144. return '%s/%s' % (path, filename)
  145. description = models.CharField(max_length=20)
  146. image = models.ImageField(storage=temp_storage, upload_to=custom_upload_path,
  147. width_field='width', height_field='height',
  148. blank=True, null=True)
  149. width = models.IntegerField(editable=False, null=True)
  150. height = models.IntegerField(editable=False, null=True)
  151. path = models.CharField(max_length=16, blank=True, default='')
  152. def __str__(self):
  153. return self.description
  154. except ImportError:
  155. test_images = False
  156. @python_2_unicode_compatible
  157. class CommaSeparatedInteger(models.Model):
  158. field = models.CommaSeparatedIntegerField(max_length=20)
  159. def __str__(self):
  160. return self.field
  161. class Homepage(models.Model):
  162. url = models.URLField()
  163. @python_2_unicode_compatible
  164. class Product(models.Model):
  165. slug = models.SlugField(unique=True)
  166. def __str__(self):
  167. return self.slug
  168. @python_2_unicode_compatible
  169. class Price(models.Model):
  170. price = models.DecimalField(max_digits=10, decimal_places=2)
  171. quantity = models.PositiveIntegerField()
  172. def __str__(self):
  173. return "%s for %s" % (self.quantity, self.price)
  174. class Meta:
  175. unique_together = (('price', 'quantity'),)
  176. class Triple(models.Model):
  177. left = models.IntegerField()
  178. middle = models.IntegerField()
  179. right = models.IntegerField()
  180. class Meta:
  181. unique_together = (('left', 'middle'), ('middle', 'right'))
  182. class ArticleStatus(models.Model):
  183. status = models.CharField(max_length=2, choices=ARTICLE_STATUS_CHAR, blank=True, null=True)
  184. @python_2_unicode_compatible
  185. class Inventory(models.Model):
  186. barcode = models.PositiveIntegerField(unique=True)
  187. parent = models.ForeignKey('self', models.SET_NULL, to_field='barcode', blank=True, null=True)
  188. name = models.CharField(blank=False, max_length=20)
  189. class Meta:
  190. ordering = ('name',)
  191. def __str__(self):
  192. return self.name
  193. def __repr__(self):
  194. return self.__str__()
  195. class Book(models.Model):
  196. title = models.CharField(max_length=40)
  197. author = models.ForeignKey(Writer, models.SET_NULL, blank=True, null=True)
  198. special_id = models.IntegerField(blank=True, null=True, unique=True)
  199. class Meta:
  200. unique_together = ('title', 'author')
  201. class BookXtra(models.Model):
  202. isbn = models.CharField(max_length=16, unique=True)
  203. suffix1 = models.IntegerField(blank=True, default=0)
  204. suffix2 = models.IntegerField(blank=True, default=0)
  205. class Meta:
  206. unique_together = (('suffix1', 'suffix2'))
  207. abstract = True
  208. class DerivedBook(Book, BookXtra):
  209. pass
  210. @python_2_unicode_compatible
  211. class ExplicitPK(models.Model):
  212. key = models.CharField(max_length=20, primary_key=True)
  213. desc = models.CharField(max_length=20, blank=True, unique=True)
  214. class Meta:
  215. unique_together = ('key', 'desc')
  216. def __str__(self):
  217. return self.key
  218. @python_2_unicode_compatible
  219. class Post(models.Model):
  220. title = models.CharField(max_length=50, unique_for_date='posted', blank=True)
  221. slug = models.CharField(max_length=50, unique_for_year='posted', blank=True)
  222. subtitle = models.CharField(max_length=50, unique_for_month='posted', blank=True)
  223. posted = models.DateField()
  224. def __str__(self):
  225. return self.title
  226. @python_2_unicode_compatible
  227. class DateTimePost(models.Model):
  228. title = models.CharField(max_length=50, unique_for_date='posted', blank=True)
  229. slug = models.CharField(max_length=50, unique_for_year='posted', blank=True)
  230. subtitle = models.CharField(max_length=50, unique_for_month='posted', blank=True)
  231. posted = models.DateTimeField(editable=False)
  232. def __str__(self):
  233. return self.title
  234. class DerivedPost(Post):
  235. pass
  236. @python_2_unicode_compatible
  237. class BigInt(models.Model):
  238. biggie = models.BigIntegerField()
  239. def __str__(self):
  240. return six.text_type(self.biggie)
  241. class MarkupField(models.CharField):
  242. def __init__(self, *args, **kwargs):
  243. kwargs["max_length"] = 20
  244. super(MarkupField, self).__init__(*args, **kwargs)
  245. def formfield(self, **kwargs):
  246. # don't allow this field to be used in form (real use-case might be
  247. # that you know the markup will always be X, but it is among an app
  248. # that allows the user to say it could be something else)
  249. # regressed at r10062
  250. return None
  251. class CustomFieldForExclusionModel(models.Model):
  252. name = models.CharField(max_length=10)
  253. markup = MarkupField()
  254. class FlexibleDatePost(models.Model):
  255. title = models.CharField(max_length=50, unique_for_date='posted', blank=True)
  256. slug = models.CharField(max_length=50, unique_for_year='posted', blank=True)
  257. subtitle = models.CharField(max_length=50, unique_for_month='posted', blank=True)
  258. posted = models.DateField(blank=True, null=True)
  259. @python_2_unicode_compatible
  260. class Colour(models.Model):
  261. name = models.CharField(max_length=50)
  262. def __iter__(self):
  263. for number in range(5):
  264. yield number
  265. def __str__(self):
  266. return self.name
  267. class ColourfulItem(models.Model):
  268. name = models.CharField(max_length=50)
  269. colours = models.ManyToManyField(Colour)
  270. class CustomErrorMessage(models.Model):
  271. name1 = models.CharField(
  272. max_length=50,
  273. validators=[validators.validate_slug],
  274. error_messages={'invalid': 'Model custom error message.'},
  275. )
  276. name2 = models.CharField(
  277. max_length=50,
  278. validators=[validators.validate_slug],
  279. error_messages={'invalid': 'Model custom error message.'},
  280. )
  281. def clean(self):
  282. if self.name1 == 'FORBIDDEN_VALUE':
  283. raise ValidationError({'name1': [ValidationError('Model.clean() error messages.')]})
  284. elif self.name1 == 'FORBIDDEN_VALUE2':
  285. raise ValidationError({'name1': 'Model.clean() error messages (simpler syntax).'})
  286. elif self.name1 == 'GLOBAL_ERROR':
  287. raise ValidationError("Global error message.")
  288. def today_callable_dict():
  289. return {"last_action__gte": datetime.datetime.today()}
  290. def today_callable_q():
  291. return models.Q(last_action__gte=datetime.datetime.today())
  292. class Character(models.Model):
  293. username = models.CharField(max_length=100)
  294. last_action = models.DateTimeField()
  295. class StumpJoke(models.Model):
  296. most_recently_fooled = models.ForeignKey(
  297. Character,
  298. models.CASCADE,
  299. limit_choices_to=today_callable_dict,
  300. related_name="+",
  301. )
  302. has_fooled_today = models.ManyToManyField(Character, limit_choices_to=today_callable_q, related_name="+")
  303. # Model for #13776
  304. class Student(models.Model):
  305. character = models.ForeignKey(Character, models.CASCADE)
  306. study = models.CharField(max_length=30)
  307. # Model for #639
  308. class Photo(models.Model):
  309. title = models.CharField(max_length=30)
  310. image = models.FileField(storage=temp_storage, upload_to='tests')
  311. # Support code for the tests; this keeps track of how many times save()
  312. # gets called on each instance.
  313. def __init__(self, *args, **kwargs):
  314. super(Photo, self).__init__(*args, **kwargs)
  315. self._savecount = 0
  316. def save(self, force_insert=False, force_update=False):
  317. super(Photo, self).save(force_insert, force_update)
  318. self._savecount += 1
  319. class UUIDPK(models.Model):
  320. uuid = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
  321. name = models.CharField(max_length=30)
  322. # Models for #24706
  323. class StrictAssignmentFieldSpecific(models.Model):
  324. title = models.CharField(max_length=30)
  325. _should_error = False
  326. def __setattr__(self, key, value):
  327. if self._should_error is True:
  328. raise ValidationError(message={key: "Cannot set attribute"}, code='invalid')
  329. super(StrictAssignmentFieldSpecific, self).__setattr__(key, value)
  330. class StrictAssignmentAll(models.Model):
  331. title = models.CharField(max_length=30)
  332. _should_error = False
  333. def __setattr__(self, key, value):
  334. if self._should_error is True:
  335. raise ValidationError(message="Cannot set attribute", code='invalid')
  336. super(StrictAssignmentAll, self).__setattr__(key, value)
  337. # A model with ForeignKey(blank=False, null=True)
  338. class Award(models.Model):
  339. name = models.CharField(max_length=30)
  340. character = models.ForeignKey(Character, models.SET_NULL, blank=False, null=True)
  341. class NullableUniqueCharFieldModel(models.Model):
  342. codename = models.CharField(max_length=50, blank=True, null=True, unique=True)