123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955 |
- import re
- import datetime
- import urlparse
- from django.conf import settings
- from django.core import mail
- from django.core.exceptions import SuspiciousOperation
- from django.core.files import temp as tempfile
- from django.core.urlresolvers import reverse
- from django.contrib.auth import REDIRECT_FIELD_NAME, admin
- from django.contrib.auth.models import User, Permission, UNUSABLE_PASSWORD
- from django.contrib.contenttypes.models import ContentType
- from django.contrib.admin.models import LogEntry, DELETION
- from django.contrib.admin.sites import LOGIN_FORM_KEY
- from django.contrib.admin.util import quote
- from django.contrib.admin.helpers import ACTION_CHECKBOX_NAME
- from django.contrib.admin.views.main import IS_POPUP_VAR
- from django.forms.util import ErrorList
- import django.template.context
- from django.test import TestCase
- from django.utils import formats
- from django.utils.cache import get_max_age
- from django.utils.encoding import iri_to_uri
- from django.utils.html import escape
- from django.utils.http import urlencode
- from django.utils.translation import activate, deactivate
- from django.utils import unittest
- from models import (Article, BarAccount, CustomArticle, EmptyModel,
- FooAccount, Gallery, ModelWithStringPrimaryKey,
- Person, Persona, Picture, Podcast, Section, Subscriber, Vodcast,
- Language, Collector, Widget, Grommet, DooHickey, FancyDoodad, Whatsit,
- Category, Post, Plot, FunkyTag, Chapter, Book, Promo, WorkHour, Employee,
- Question, Answer, Inquisition, Actor, FoodDelivery,
- RowLevelChangePermissionModel, Paper, CoverLetter, Story, OtherStory)
- class AdminViewBasicTest(TestCase):
- fixtures = ['admin-views-users.xml', 'admin-views-colors.xml',
- 'admin-views-fabrics.xml', 'admin-views-books.xml']
-
-
-
- urlbit = 'admin'
- def setUp(self):
- self.old_USE_I18N = settings.USE_I18N
- self.old_USE_L10N = settings.USE_L10N
- self.old_LANGUAGE_CODE = settings.LANGUAGE_CODE
- self.client.login(username='super', password='secret')
- settings.USE_I18N = True
- def tearDown(self):
- settings.USE_I18N = self.old_USE_I18N
- settings.USE_L10N = self.old_USE_L10N
- settings.LANGUAGE_CODE = self.old_LANGUAGE_CODE
- self.client.logout()
- formats.reset_format_cache()
- def testTrailingSlashRequired(self):
- """
- If you leave off the trailing slash, app should redirect and add it.
- """
- request = self.client.get('/test_admin/%s/admin_views/article/add' % self.urlbit)
- self.assertRedirects(request,
- '/test_admin/%s/admin_views/article/add/' % self.urlbit, status_code=301
- )
- def testBasicAddGet(self):
- """
- A smoke test to ensure GET on the add_view works.
- """
- response = self.client.get('/test_admin/%s/admin_views/section/add/' % self.urlbit)
- self.assertEqual(response.status_code, 200)
- def testAddWithGETArgs(self):
- response = self.client.get('/test_admin/%s/admin_views/section/add/' % self.urlbit, {'name': 'My Section'})
- self.assertEqual(response.status_code, 200)
- self.assertTrue(
- 'value="My Section"' in response.content,
- "Couldn't find an input with the right value in the response."
- )
- def testBasicEditGet(self):
- """
- A smoke test to ensure GET on the change_view works.
- """
- response = self.client.get('/test_admin/%s/admin_views/section/1/' % self.urlbit)
- self.assertEqual(response.status_code, 200)
- def testBasicEditGetStringPK(self):
- """
- A smoke test to ensure GET on the change_view works (returns an HTTP
- 404 error, see #11191) when passing a string as the PK argument for a
- model with an integer PK field.
- """
- response = self.client.get('/test_admin/%s/admin_views/section/abc/' % self.urlbit)
- self.assertEqual(response.status_code, 404)
- def testBasicAddPost(self):
- """
- A smoke test to ensure POST on add_view works.
- """
- post_data = {
- "name": u"Another Section",
-
- "article_set-TOTAL_FORMS": u"3",
- "article_set-INITIAL_FORMS": u"0",
- "article_set-MAX_NUM_FORMS": u"0",
- }
- response = self.client.post('/test_admin/%s/admin_views/section/add/' % self.urlbit, post_data)
- self.assertEqual(response.status_code, 302)
- def testPopupAddPost(self):
- """
- Ensure http response from a popup is properly escaped.
- """
- post_data = {
- '_popup': u'1',
- 'title': u'title with a new\nline',
- 'content': u'some content',
- 'date_0': u'2010-09-10',
- 'date_1': u'14:55:39',
- }
- response = self.client.post('/test_admin/%s/admin_views/article/add/' % self.urlbit, post_data)
- self.failUnlessEqual(response.status_code, 200)
- self.assertContains(response, 'dismissAddAnotherPopup')
- self.assertContains(response, 'title with a new\u000Aline')
-
- inline_post_data = {
- "name": u"Test section",
-
- "article_set-TOTAL_FORMS": u"6",
- "article_set-INITIAL_FORMS": u"3",
- "article_set-MAX_NUM_FORMS": u"0",
- "article_set-0-id": u"1",
-
- "article_set-0-title": u"Norske bostaver æøå skaper problemer",
- "article_set-0-content": u"<p>Middle content</p>",
- "article_set-0-date_0": u"2008-03-18",
- "article_set-0-date_1": u"11:54:58",
- "article_set-0-section": u"1",
- "article_set-1-id": u"2",
- "article_set-1-title": u"Need a title.",
- "article_set-1-content": u"<p>Oldest content</p>",
- "article_set-1-date_0": u"2000-03-18",
- "article_set-1-date_1": u"11:54:58",
- "article_set-2-id": u"3",
- "article_set-2-title": u"Need a title.",
- "article_set-2-content": u"<p>Newest content</p>",
- "article_set-2-date_0": u"2009-03-18",
- "article_set-2-date_1": u"11:54:58",
- "article_set-3-id": u"",
- "article_set-3-title": u"",
- "article_set-3-content": u"",
- "article_set-3-date_0": u"",
- "article_set-3-date_1": u"",
- "article_set-4-id": u"",
- "article_set-4-title": u"",
- "article_set-4-content": u"",
- "article_set-4-date_0": u"",
- "article_set-4-date_1": u"",
- "article_set-5-id": u"",
- "article_set-5-title": u"",
- "article_set-5-content": u"",
- "article_set-5-date_0": u"",
- "article_set-5-date_1": u"",
- }
- def testBasicEditPost(self):
- """
- A smoke test to ensure POST on edit_view works.
- """
- response = self.client.post('/test_admin/%s/admin_views/section/1/' % self.urlbit, self.inline_post_data)
- self.assertEqual(response.status_code, 302)
- def testEditSaveAs(self):
- """
- Test "save as".
- """
- post_data = self.inline_post_data.copy()
- post_data.update({
- '_saveasnew': u'Save+as+new',
- "article_set-1-section": u"1",
- "article_set-2-section": u"1",
- "article_set-3-section": u"1",
- "article_set-4-section": u"1",
- "article_set-5-section": u"1",
- })
- response = self.client.post('/test_admin/%s/admin_views/section/1/' % self.urlbit, post_data)
- self.assertEqual(response.status_code, 302)
- def testChangeListSortingCallable(self):
- """
- Ensure we can sort on a list_display field that is a callable
- (column 2 is callable_year in ArticleAdmin)
- """
- response = self.client.get('/test_admin/%s/admin_views/article/' % self.urlbit, {'ot': 'asc', 'o': 2})
- self.assertEqual(response.status_code, 200)
- self.assertTrue(
- response.content.index('Oldest content') < response.content.index('Middle content') and
- response.content.index('Middle content') < response.content.index('Newest content'),
- "Results of sorting on callable are out of order."
- )
- def testChangeListSortingModel(self):
- """
- Ensure we can sort on a list_display field that is a Model method
- (colunn 3 is 'model_year' in ArticleAdmin)
- """
- response = self.client.get('/test_admin/%s/admin_views/article/' % self.urlbit, {'ot': 'dsc', 'o': 3})
- self.assertEqual(response.status_code, 200)
- self.assertTrue(
- response.content.index('Newest content') < response.content.index('Middle content') and
- response.content.index('Middle content') < response.content.index('Oldest content'),
- "Results of sorting on Model method are out of order."
- )
- def testChangeListSortingModelAdmin(self):
- """
- Ensure we can sort on a list_display field that is a ModelAdmin method
- (colunn 4 is 'modeladmin_year' in ArticleAdmin)
- """
- response = self.client.get('/test_admin/%s/admin_views/article/' % self.urlbit, {'ot': 'asc', 'o': 4})
- self.assertEqual(response.status_code, 200)
- self.assertTrue(
- response.content.index('Oldest content') < response.content.index('Middle content') and
- response.content.index('Middle content') < response.content.index('Newest content'),
- "Results of sorting on ModelAdmin method are out of order."
- )
- def testLimitedFilter(self):
- """Ensure admin changelist filters do not contain objects excluded via limit_choices_to.
- This also tests relation-spanning filters (e.g. 'color__value').
- """
- response = self.client.get('/test_admin/%s/admin_views/thing/' % self.urlbit)
- self.assertEqual(response.status_code, 200)
- self.assertTrue(
- '<div id="changelist-filter">' in response.content,
- "Expected filter not found in changelist view."
- )
- self.assertFalse(
- '<a href="?color__id__exact=3">Blue</a>' in response.content,
- "Changelist filter not correctly limited by limit_choices_to."
- )
- def testRelationSpanningFilters(self):
- response = self.client.get('/test_admin/%s/admin_views/chapterxtra1/' %
- self.urlbit)
- self.assertEqual(response.status_code, 200)
- self.assertContains(response, '<div id="changelist-filter">')
- filters = {
- 'chap__id__exact': dict(
- values=[c.id for c in Chapter.objects.all()],
- test=lambda obj, value: obj.chap.id == value),
- 'chap__title': dict(
- values=[c.title for c in Chapter.objects.all()],
- test=lambda obj, value: obj.chap.title == value),
- 'chap__book__id__exact': dict(
- values=[b.id for b in Book.objects.all()],
- test=lambda obj, value: obj.chap.book.id == value),
- 'chap__book__name': dict(
- values=[b.name for b in Book.objects.all()],
- test=lambda obj, value: obj.chap.book.name == value),
- 'chap__book__promo__id__exact': dict(
- values=[p.id for p in Promo.objects.all()],
- test=lambda obj, value:
- obj.chap.book.promo_set.filter(id=value).exists()),
- 'chap__book__promo__name': dict(
- values=[p.name for p in Promo.objects.all()],
- test=lambda obj, value:
- obj.chap.book.promo_set.filter(name=value).exists()),
- }
- for filter_path, params in filters.items():
- for value in params['values']:
- query_string = urlencode({filter_path: value})
-
- self.assertContains(response, '<a href="?%s">' % query_string)
-
- filtered_response = self.client.get(
- '/test_admin/%s/admin_views/chapterxtra1/?%s' % (
- self.urlbit, query_string))
- self.assertEqual(filtered_response.status_code, 200)
-
- for obj in filtered_response.context['cl'].query_set.all():
- self.assertTrue(params['test'](obj, value))
- def testIncorrectLookupParameters(self):
- """Ensure incorrect lookup parameters are handled gracefully."""
- response = self.client.get('/test_admin/%s/admin_views/thing/' % self.urlbit, {'notarealfield': '5'})
- self.assertRedirects(response, '/test_admin/%s/admin_views/thing/?e=1' % self.urlbit)
- response = self.client.get('/test_admin/%s/admin_views/thing/' % self.urlbit, {'color__id__exact': 'StringNotInteger!'})
- self.assertRedirects(response, '/test_admin/%s/admin_views/thing/?e=1' % self.urlbit)
- def testIsNullLookups(self):
- """Ensure is_null is handled correctly."""
- Article.objects.create(title="I Could Go Anywhere", content="Versatile", date=datetime.datetime.now())
- response = self.client.get('/test_admin/%s/admin_views/article/' % self.urlbit)
- self.assertTrue('4 articles' in response.content, '"4 articles" missing from response')
- response = self.client.get('/test_admin/%s/admin_views/article/' % self.urlbit, {'section__isnull': 'false'})
- self.assertTrue('3 articles' in response.content, '"3 articles" missing from response')
- response = self.client.get('/test_admin/%s/admin_views/article/' % self.urlbit, {'section__isnull': 'true'})
- self.assertTrue('1 article' in response.content, '"1 article" missing from response')
- def testLogoutAndPasswordChangeURLs(self):
- response = self.client.get('/test_admin/%s/admin_views/article/' % self.urlbit)
- self.assertFalse('<a href="/test_admin/%s/logout/">' % self.urlbit not in response.content)
- self.assertFalse('<a href="/test_admin/%s/password_change/">' % self.urlbit not in response.content)
- def testNamedGroupFieldChoicesChangeList(self):
- """
- Ensures the admin changelist shows correct values in the relevant column
- for rows corresponding to instances of a model in which a named group
- has been used in the choices option of a field.
- """
- response = self.client.get('/test_admin/%s/admin_views/fabric/' % self.urlbit)
- self.assertEqual(response.status_code, 200)
- self.assertTrue(
- '<a href="1/">Horizontal</a>' in response.content and
- '<a href="2/">Vertical</a>' in response.content,
- "Changelist table isn't showing the right human-readable values set by a model field 'choices' option named group."
- )
- def testNamedGroupFieldChoicesFilter(self):
- """
- Ensures the filter UI shows correctly when at least one named group has
- been used in the choices option of a model field.
- """
- response = self.client.get('/test_admin/%s/admin_views/fabric/' % self.urlbit)
- self.assertEqual(response.status_code, 200)
- self.assertTrue(
- '<div id="changelist-filter">' in response.content,
- "Expected filter not found in changelist view."
- )
- self.assertTrue(
- '<a href="?surface__exact=x">Horizontal</a>' in response.content and
- '<a href="?surface__exact=y">Vertical</a>' in response.content,
- "Changelist filter isn't showing options contained inside a model field 'choices' option named group."
- )
- def testChangeListNullBooleanDisplay(self):
- Post.objects.create(public=None)
-
-
-
- response = self.client.get("/test_admin/admin/admin_views/post/")
- self.assertTrue('icon-unknown.gif' in response.content)
- def testI18NLanguageNonEnglishDefault(self):
- """
- Check if the Javascript i18n view returns an empty language catalog
- if the default language is non-English but the selected language
- is English. See #13388 and #3594 for more details.
- """
- try:
- settings.LANGUAGE_CODE = 'fr'
- activate('en-us')
- response = self.client.get('/test_admin/admin/jsi18n/')
- self.assertNotContains(response, 'Choisir une heure')
- finally:
- deactivate()
- def testI18NLanguageNonEnglishFallback(self):
- """
- Makes sure that the fallback language is still working properly
- in cases where the selected language cannot be found.
- """
- try:
- settings.LANGUAGE_CODE = 'fr'
- activate('none')
- response = self.client.get('/test_admin/admin/jsi18n/')
- self.assertContains(response, 'Choisir une heure')
- finally:
- deactivate()
- def testL10NDeactivated(self):
- """
- Check if L10N is deactivated, the Javascript i18n view doesn't
- return localized date/time formats. Refs #14824.
- """
- try:
- settings.LANGUAGE_CODE = 'ru'
- settings.USE_L10N = False
- activate('ru')
- response = self.client.get('/test_admin/admin/jsi18n/')
- self.assertNotContains(response, '%d.%m.%Y %H:%M:%S')
- self.assertContains(response, '%Y-%m-%d %H:%M:%S')
- finally:
- deactivate()
- def test_disallowed_filtering(self):
- self.assertRaises(SuspiciousOperation,
- self.client.get, "/test_admin/admin/admin_views/album/?owner__email__startswith=fuzzy"
- )
- try:
- self.client.get("/test_admin/admin/admin_views/thing/?color__value__startswith=red")
- self.client.get("/test_admin/admin/admin_views/thing/?color__value=red")
- except SuspiciousOperation:
- self.fail("Filters are allowed if explicitly included in list_filter")
- try:
- self.client.get("/test_admin/admin/admin_views/person/?age__gt=30")
- except SuspiciousOperation:
- self.fail("Filters should be allowed if they involve a local field without the need to whitelist them in list_filter or date_hierarchy.")
- e1 = Employee.objects.create(name='Anonymous', gender=1, age=22, alive=True, code='123')
- e2 = Employee.objects.create(name='Visitor', gender=2, age=19, alive=True, code='124')
- WorkHour.objects.create(datum=datetime.datetime.now(), employee=e1)
- WorkHour.objects.create(datum=datetime.datetime.now(), employee=e2)
- response = self.client.get("/test_admin/admin/admin_views/workhour/")
- self.assertEqual(response.status_code, 200)
- self.assertContains(response, 'employee__person_ptr__exact')
- response = self.client.get("/test_admin/admin/admin_views/workhour/?employee__person_ptr__exact=%d" % e1.pk)
- self.assertEqual(response.status_code, 200)
- def test_allowed_filtering_15103(self):
- """
- Regressions test for ticket 15103 - filtering on fields defined in a
- ForeignKey 'limit_choices_to' should be allowed, otherwise raw_id_fields
- can break.
- """
- try:
- self.client.get("/test_admin/admin/admin_views/inquisition/?leader__name=Palin&leader__age=27")
- except SuspiciousOperation:
- self.fail("Filters should be allowed if they are defined on a ForeignKey pointing to this model")
- class AdminJavaScriptTest(AdminViewBasicTest):
- def testSingleWidgetFirsFieldFocus(self):
- """
- JavaScript-assisted auto-focus on first field.
- """
- response = self.client.get('/test_admin/%s/admin_views/picture/add/' % self.urlbit)
- self.assertContains(
- response,
- '<script type="text/javascript">document.getElementById("id_name").focus();</script>'
- )
- def testMultiWidgetFirsFieldFocus(self):
- """
- JavaScript-assisted auto-focus should work if a model/ModelAdmin setup
- is such that the first form field has a MultiWidget.
- """
- response = self.client.get('/test_admin/%s/admin_views/reservation/add/' % self.urlbit)
- self.assertContains(
- response,
- '<script type="text/javascript">document.getElementById("id_start_date_0").focus();</script>'
- )
- class SaveAsTests(TestCase):
- fixtures = ['admin-views-users.xml','admin-views-person.xml']
- def setUp(self):
- self.client.login(username='super', password='secret')
- def tearDown(self):
- self.client.logout()
- def test_save_as_duplication(self):
- """Ensure save as actually creates a new person"""
- post_data = {'_saveasnew':'', 'name':'John M', 'gender':1, 'age': 42}
- response = self.client.post('/test_admin/admin/admin_views/person/1/', post_data)
- self.assertEqual(len(Person.objects.filter(name='John M')), 1)
- self.assertEqual(len(Person.objects.filter(id=1)), 1)
- def test_save_as_display(self):
- """
- Ensure that 'save as' is displayed when activated and after submitting
- invalid data aside save_as_new will not show us a form to overwrite the
- initial model.
- """
- response = self.client.get('/test_admin/admin/admin_views/person/1/')
- self.assertTrue(response.context['save_as'])
- post_data = {'_saveasnew':'', 'name':'John M', 'gender':3, 'alive':'checked'}
- response = self.client.post('/test_admin/admin/admin_views/person/1/', post_data)
- self.assertEqual(response.context['form_url'], '../add/')
- class CustomModelAdminTest(AdminViewBasicTest):
- urlbit = "admin2"
- def testCustomAdminSiteLoginForm(self):
- self.client.logout()
- request = self.client.get('/test_admin/admin2/')
- self.assertEqual(request.status_code, 200)
- login = self.client.post('/test_admin/admin2/', {
- REDIRECT_FIELD_NAME: '/test_admin/admin2/',
- LOGIN_FORM_KEY: 1,
- 'username': 'customform',
- 'password': 'secret',
- })
- self.assertEqual(login.status_code, 200)
- self.assertContains(login, 'custom form error')
- def testCustomAdminSiteLoginTemplate(self):
- self.client.logout()
- request = self.client.get('/test_admin/admin2/')
- self.assertTemplateUsed(request, 'custom_admin/login.html')
- self.assertTrue('Hello from a custom login template' in request.content)
- def testCustomAdminSiteLogoutTemplate(self):
- request = self.client.get('/test_admin/admin2/logout/')
- self.assertTemplateUsed(request, 'custom_admin/logout.html')
- self.assertTrue('Hello from a custom logout template' in request.content)
- def testCustomAdminSiteIndexViewAndTemplate(self):
- request = self.client.get('/test_admin/admin2/')
- self.assertTemplateUsed(request, 'custom_admin/index.html')
- self.assertTrue('Hello from a custom index template *bar*' in request.content)
- def testCustomAdminSitePasswordChangeTemplate(self):
- request = self.client.get('/test_admin/admin2/password_change/')
- self.assertTemplateUsed(request, 'custom_admin/password_change_form.html')
- self.assertTrue('Hello from a custom password change form template' in request.content)
- def testCustomAdminSitePasswordChangeDoneTemplate(self):
- request = self.client.get('/test_admin/admin2/password_change/done/')
- self.assertTemplateUsed(request, 'custom_admin/password_change_done.html')
- self.assertTrue('Hello from a custom password change done template' in request.content)
- def testCustomAdminSiteView(self):
- self.client.login(username='super', password='secret')
- response = self.client.get('/test_admin/%s/my_view/' % self.urlbit)
- self.assertTrue(response.content == "Django is a magical pony!", response.content)
- def get_perm(Model, perm):
- """Return the permission object, for the Model"""
- ct = ContentType.objects.get_for_model(Model)
- return Permission.objects.get(content_type=ct, codename=perm)
- class AdminViewPermissionsTest(TestCase):
- """Tests for Admin Views Permissions."""
- fixtures = ['admin-views-users.xml']
- def setUp(self):
- """Test setup."""
-
-
-
- opts = Article._meta
-
- add_user = User.objects.get(username='adduser')
- add_user.user_permissions.add(get_perm(Article,
- opts.get_add_permission()))
-
- change_user = User.objects.get(username='changeuser')
- change_user.user_permissions.add(get_perm(Article,
- opts.get_change_permission()))
-
- delete_user = User.objects.get(username='deleteuser')
- delete_user.user_permissions.add(get_perm(Article,
- opts.get_delete_permission()))
- delete_user.user_permissions.add(get_perm(Section,
- Section._meta.get_delete_permission()))
-
- self.super_login = {
- REDIRECT_FIELD_NAME: '/test_admin/admin/',
- LOGIN_FORM_KEY: 1,
- 'username': 'super',
- 'password': 'secret',
- }
- self.super_email_login = {
- REDIRECT_FIELD_NAME: '/test_admin/admin/',
- LOGIN_FORM_KEY: 1,
- 'username': 'super@example.com',
- 'password': 'secret',
- }
- self.super_email_bad_login = {
- REDIRECT_FIELD_NAME: '/test_admin/admin/',
- LOGIN_FORM_KEY: 1,
- 'username': 'super@example.com',
- 'password': 'notsecret',
- }
- self.adduser_login = {
- REDIRECT_FIELD_NAME: '/test_admin/admin/',
- LOGIN_FORM_KEY: 1,
- 'username': 'adduser',
- 'password': 'secret',
- }
- self.changeuser_login = {
- REDIRECT_FIELD_NAME: '/test_admin/admin/',
- LOGIN_FORM_KEY: 1,
- 'username': 'changeuser',
- 'password': 'secret',
- }
- self.deleteuser_login = {
- REDIRECT_FIELD_NAME: '/test_admin/admin/',
- LOGIN_FORM_KEY: 1,
- 'username': 'deleteuser',
- 'password': 'secret',
- }
- self.joepublic_login = {
- REDIRECT_FIELD_NAME: '/test_admin/admin/',
- LOGIN_FORM_KEY: 1,
- 'username': 'joepublic',
- 'password': 'secret',
- }
- self.no_username_login = {
- REDIRECT_FIELD_NAME: '/test_admin/admin/',
- LOGIN_FORM_KEY: 1,
- 'password': 'secret',
- }
- def testLogin(self):
- """
- Make sure only staff members can log in.
- Successful posts to the login page will redirect to the orignal url.
- Unsuccessfull attempts will continue to render the login page with
- a 200 status code.
- """
-
- request = self.client.get('/test_admin/admin/')
- self.assertEqual(request.status_code, 200)
- login = self.client.post('/test_admin/admin/', self.super_login)
- self.assertRedirects(login, '/test_admin/admin/')
- self.assertFalse(login.context)
- self.client.get('/test_admin/admin/logout/')
-
- request = self.client.get('/test_admin/admin/')
- self.assertEqual(request.status_code, 200)
- login = self.client.post('/test_admin/admin/', self.super_email_login)
- self.assertContains(login, "Your e-mail address is not your username")
-
- login = self.client.post('/test_admin/admin/', self.super_email_bad_login)
- self.assertContains(login, "Please enter a correct username and password.")
- new_user = User(username='jondoe', password='secret', email='super@example.com')
- new_user.save()
-
- login = self.client.post('/test_admin/admin/', self.super_email_login)
- self.assertContains(login, "Please enter a correct username and password.")
-
- request = self.client.get('/test_admin/admin/')
- self.assertEqual(request.status_code, 200)
- login = self.client.post('/test_admin/admin/', self.adduser_login)
- self.assertRedirects(login, '/test_admin/admin/')
- self.assertFalse(login.context)
- self.client.get('/test_admin/admin/logout/')
-
- request = self.client.get('/test_admin/admin/')
- self.assertEqual(request.status_code, 200)
- login = self.client.post('/test_admin/admin/', self.changeuser_login)
- self.assertRedirects(login, '/test_admin/admin/')
- self.assertFalse(login.context)
- self.client.get('/test_admin/admin/logout/')
-
- request = self.client.get('/test_admin/admin/')
- self.assertEqual(request.status_code, 200)
- login = self.client.post('/test_admin/admin/', self.deleteuser_login)
- self.assertRedirects(login, '/test_admin/admin/')
- self.assertFalse(login.context)
- self.client.get('/test_admin/admin/logout/')
-
- request = self.client.get('/test_admin/admin/')
- self.assertEqual(request.status_code, 200)
- login = self.client.post('/test_admin/admin/', self.joepublic_login)
- self.assertEqual(login.status_code, 200)
- self.assertContains(login, "Please enter a correct username and password.")
-
- request = self.client.get('/test_admin/admin/')
- self.assertEqual(request.status_code, 200)
- login = self.client.post('/test_admin/admin/', self.no_username_login)
- self.assertEqual(login.status_code, 200)
- form = login.context[0].get('form')
- self.assertEqual(form.errors['username'][0], 'This field is required.')
- def testLoginSuccessfullyRedirectsToOriginalUrl(self):
- request = self.client.get('/test_admin/admin/')
- self.assertEqual(request.status_code, 200)
- query_string = 'the-answer=42'
- redirect_url = '/test_admin/admin/?%s' % query_string
- new_next = {REDIRECT_FIELD_NAME: redirect_url}
- login = self.client.post('/test_admin/admin/', dict(self.super_login, **new_next), QUERY_STRING=query_string)
- self.assertRedirects(login, redirect_url)
- def testAddView(self):
- """Test add view restricts access and actually adds items."""
- add_dict = {'title' : 'Døm ikke',
- 'content': '<p>great article</p>',
- 'date_0': '2008-03-18', 'date_1': '10:54:39',
- 'section': 1}
-
- self.client.get('/test_admin/admin/')
- self.client.post('/test_admin/admin/', self.changeuser_login)
-
- self.assertEqual(self.client.session.test_cookie_worked(), False)
- request = self.client.get('/test_admin/admin/admin_views/article/add/')
- self.assertEqual(request.status_code, 403)
-
- post = self.client.post('/test_admin/admin/admin_views/article/add/', add_dict)
- self.assertEqual(post.status_code, 403)
- self.assertEqual(Article.objects.all().count(), 3)
- self.client.get('/test_admin/admin/logout/')
-
- self.client.get('/test_admin/admin/')
- self.client.post('/test_admin/admin/', self.adduser_login)
- addpage = self.client.get('/test_admin/admin/admin_views/article/add/')
- self.assertEqual(addpage.status_code, 200)
- change_list_link = '<a href="../">Articles</a> ›'
- self.assertFalse(change_list_link in addpage.content,
- 'User restricted to add permission is given link to change list view in breadcrumbs.')
- post = self.client.post('/test_admin/admin/admin_views/article/add/', add_dict)
- self.assertRedirects(post, '/test_admin/admin/')
- self.assertEqual(Article.objects.all().count(), 4)
- self.assertEqual(len(mail.outbox), 1)
- self.assertEqual(mail.outbox[0].subject, 'Greetings from a created object')
- self.client.get('/test_admin/admin/logout/')
-
- self.client.get('/test_admin/admin/')
- self.client.post('/test_admin/admin/', self.super_login)
- addpage = self.client.get('/test_admin/admin/admin_views/article/add/')
- self.assertEqual(addpage.status_code, 200)
- self.assertFalse(change_list_link not in addpage.content,
- 'Unrestricted user is not given link to change list view in breadcrumbs.')
- post = self.client.post('/test_admin/admin/admin_views/article/add/', add_dict)
- self.assertRedirects(post, '/test_admin/admin/admin_views/article/')
- self.assertEqual(Article.objects.all().count(), 5)
- self.client.get('/test_admin/admin/logout/')
-
-
- login = self.client.login(username='joepublic', password='secret')
-
- self.client.get('/test_admin/admin/')
- self.client.post('/test_admin/admin/', self.super_login)
-
- self.assertEqual(self.client.session.test_cookie_worked(), False)
- def testChangeView(self):
- """Change view should restrict access and allow users to edit items."""
- change_dict = {'title' : 'Ikke fordømt',
- 'content': '<p>edited article</p>',
- 'date_0': '2008-03-18', 'date_1': '10:54:39',
- 'section': 1}
-
- self.client.get('/test_admin/admin/')
- self.client.post('/test_admin/admin/', self.adduser_login)
- request = self.client.get('/test_admin/admin/admin_views/article/')
- self.assertEqual(request.status_code, 403)
- request = self.client.get('/test_admin/admin/admin_views/article/1/')
- self.assertEqual(request.status_code, 403)
- post = self.client.post('/test_admin/admin/admin_views/article/1/', change_dict)
- self.assertEqual(post.status_code, 403)
- self.client.get('/test_admin/admin/logout/')
-
- self.client.get('/test_admin/admin/')
- self.client.post('/test_admin/admin/', self.changeuser_login)
- request = self.client.get('/test_admin/admin/admin_views/article/')
- self.assertEqual(request.status_code, 200)
- request = self.client.get('/test_admin/admin/admin_views/article/1/')
- self.assertEqual(request.status_code, 200)
- post = self.client.post('/test_admin/admin/admin_views/article/1/', change_dict)
- self.assertRedirects(post, '/test_admin/admin/admin_views/article/')
- self.assertEqual(Article.objects.get(pk=1).content, '<p>edited article</p>')
-
- change_dict['title'] = ''
- post = self.client.post('/test_admin/admin/admin_views/article/1/', change_dict)
- self.assertEqual(request.status_code, 200)
- self.assertTrue('Please correct the error below.' in post.content,
- 'Singular error message not found in response to post with one error.')
- change_dict['content'] = ''
- post = self.client.post('/test_admin/admin/admin_views/article/1/', change_dict)
- self.assertEqual(request.status_code, 200)
- self.assertTrue('Please correct the errors below.' in post.content,
- 'Plural error message not found in response to post with multiple errors.')
- self.client.get('/test_admin/admin/logout/')
-
- RowLevelChangePermissionModel.objects.create(name="odd id")
- RowLevelChangePermissionModel.objects.create(name="even id")
- for login_dict in [self.super_login, self.changeuser_login, self.adduser_login, self.deleteuser_login]:
- self.client.post('/test_admin/admin/', login_dict)
- request = self.client.get('/test_admin/admin/admin_views/rowlevelchangepermissionmodel/1/')
- self.assertEqual(request.status_code, 403)
- request = self.client.post('/test_admin/admin/admin_views/rowlevelchangepermissionmodel/1/', {'name': 'changed'})
- self.assertEqual(RowLevelChangePermissionModel.objects.get(id=1).name, 'odd id')
- self.assertEqual(request.status_code, 403)
- request = self.client.get('/test_admin/admin/admin_views/rowlevelchangepermissionmodel/2/')
- self.assertEqual(request.status_code, 200)
- request = self.client.post('/test_admin/admin/admin_views/rowlevelchangepermissionmodel/2/', {'name': 'changed'})
- self.assertEqual(RowLevelChangePermissionModel.objects.get(id=2).name, 'changed')
- self.assertRedirects(request, '/test_admin/admin/')
- self.client.get('/test_admin/admin/logout/')
- for login_dict in [self.joepublic_login, self.no_username_login]:
- self.client.post('/test_admin/admin/', login_dict)
- request = self.client.get('/test_admin/admin/admin_views/rowlevelchangepermissionmodel/1/')
- self.assertEqual(request.status_code, 200)
- self.assertContains(request, 'login-form')
- request = self.client.post('/test_admin/admin/admin_views/rowlevelchangepermissionmodel/1/', {'name': 'changed'})
- self.assertEqual(RowLevelChangePermissionModel.objects.get(id=1).name, 'odd id')
- self.assertEqual(request.status_code, 200)
- self.assertContains(request, 'login-form')
- request = self.client.get('/test_admin/admin/admin_views/rowlevelchangepermissionmodel/2/')
- self.assertEqual(request.status_code, 200)
- self.assertContains(request, 'login-form')
- request = self.client.post('/test_admin/admin/admin_views/rowlevelchangepermissionmodel/2/', {'name': 'changed again'})
- self.assertEqual(RowLevelChangePermissionModel.objects.get(id=2).name, 'changed')
- self.assertEqual(request.status_code, 200)
- self.assertContains(request, 'login-form')
- self.client.get('/test_admin/admin/logout/')
- def testConditionallyShowAddSectionLink(self):
- """
- The foreign key widget should only show the "add related" button if the
- user has permission to add that related item.
- """
-
- url = '/test_admin/admin/admin_views/article/add/'
- add_link_text = ' class="add-another"'
- self.client.get('/test_admin/admin/')
- self.client.post('/test_admin/admin/', self.adduser_login)
-
-
- response = self.client.get(url)
- self.assertNotContains(response, add_link_text)
-
-
- add_user = User.objects.get(username='adduser')
- perm = get_perm(Section, Section._meta.get_add_permission())
- add_user.user_permissions.add(perm)
- response = self.client.get(url)
- self.assertContains(response, add_link_text)
- def testCustomModelAdminTemplates(self):
- self.client.get('/test_admin/admin/')
- self.client.post('/test_admin/admin/', self.super_login)
-
- request = self.client.get('/test_admin/admin/admin_views/customarticle/')
- self.assertEqual(request.status_code, 200)
- self.assertTrue("var hello = 'Hello!';" in request.content)
- self.assertTemplateUsed(request, 'custom_admin/change_list.html')
-
- request = self.client.get('/test_admin/admin/admin_views/customarticle/add/')
- self.assertTemplateUsed(request, 'custom_admin/add_form.html')
-
- post = self.client.post('/test_admin/admin/admin_views/customarticle/add/', {
- 'content': '<p>great article</p>',
- 'date_0': '2008-03-18',
- 'date_1': '10:54:39'
- })
- self.assertRedirects(post, '/test_admin/admin/admin_views/customarticle/')
- self.assertEqual(CustomArticle.objects.all().count(), 1)
-
-
- request = self.client.get('/test_admin/admin/admin_views/customarticle/1/')
- self.assertTemplateUsed(request, 'custom_admin/change_form.html')
- request = self.client.get('/test_admin/admin/admin_views/customarticle/1/delete/')
- self.assertTemplateUsed(request, 'custom_admin/delete_confirmation.html')
- request = self.client.post('/test_admin/admin/admin_views/customarticle/', data={
- 'index': 0,
- 'action': ['delete_selected'],
- '_selected_action': ['1'],
- })
- self.assertTemplateUsed(request, 'custom_admin/delete_selected_confirmation.html')
- request = self.client.get('/test_admin/admin/admin_views/customarticle/1/history/')
- self.assertTemplateUsed(request, 'custom_admin/object_history.html')
- self.client.get('/test_admin/admin/logout/')
- def testDeleteView(self):
- """Delete view should restrict access and actually delete items."""
- delete_dict = {'post': 'yes'}
-
- self.client.get('/test_admin/admin/')
- self.client.post('/test_admin/admin/', self.adduser_login)
- request = self.client.get('/test_admin/admin/admin_views/article/1/delete/')
- self.assertEqual(request.status_code, 403)
- post = self.client.post('/test_admin/admin/admin_views/article/1/delete/', delete_dict)
- self.assertEqual(post.status_code, 403)
- self.assertEqual(Article.objects.all().count(), 3)
- self.client.get('/test_admin/admin/logout/')
-
- self.client.get('/test_admin/admin/')
- self.client.post('/test_admin/admin/', self.deleteuser_login)
- response = self.client.get('/test_admin/admin/admin_views/section/1/delete/')
-
- self.assertContains(response, "admin_views/article/1/")
- response = self.client.get('/test_admin/admin/admin_views/article/1/delete/')
- self.assertEqual(response.status_code, 200)
- post = self.client.post('/test_admin/admin/admin_views/article/1/delete/', delete_dict)
- self.assertRedirects(post, '/test_admin/admin/')
- self.assertEqual(Article.objects.all().count(), 2)
- self.assertEqual(len(mail.outbox), 1)
- self.assertEqual(mail.outbox[0].subject, 'Greetings from a deleted object')
- article_ct = ContentType.objects.get_for_model(Article)
- logged = LogEntry.objects.get(content_type=article_ct, action_flag=DELETION)
- self.assertEqual(logged.object_id, u'1')
- self.client.get('/test_admin/admin/logout/')
- def testDisabledPermissionsWhenLoggedIn(self):
- self.client.login(username='super', password='secret')
- superuser = User.objects.get(username='super')
- superuser.is_active = False
- superuser.save()
- response = self.client.get('/test_admin/admin/')
- self.assertContains(response, 'id="login-form"')
- self.assertNotContains(response, 'Log out')
- response = self.client.get('/test_admin/admin/secure-view/')
- self.assertContains(response, 'id="login-form"')
- class AdminViewDeletedObjectsTest(TestCase):
- fixtures = ['admin-views-users.xml', 'deleted-objects.xml']
- def setUp(self):
- self.client.login(username='super', password='secret')
- def tearDown(self):
- self.client.logout()
- def test_nesting(self):
- """
- Objects should be nested to display the relationships that
- cause them to be scheduled for deletion.
- """
- pattern = re.compile(r"""<li>Plot: <a href=".+/admin_views/plot/1/">World Domination</a>\s*<ul>\s*<li>Plot details: <a href=".+/admin_views/plotdetails/1/">almost finished</a>""")
- response = self.client.get('/test_admin/admin/admin_views/villain/%s/delete/' % quote(1))
- self.assertTrue(pattern.search(response.content))
- def test_cyclic(self):
- """
- Cyclic relationships should still cause each object to only be
- listed once.
- """
- one = """<li>Cyclic one: <a href="/test_admin/admin/admin_views/cyclicone/1/">I am recursive</a>"""
- two = """<li>Cyclic two: <a href="/test_admin/admin/admin_views/cyclictwo/1/">I am recursive too</a>"""
- response = self.client.get('/test_admin/admin/admin_views/cyclicone/%s/delete/' % quote(1))
- self.assertContains(response, one, 1)
- self.assertContains(response, two, 1)
- def test_perms_needed(self):
- self.client.logout()
- delete_user = User.objects.get(username='deleteuser')
- delete_user.user_permissions.add(get_perm(Plot,
- Plot._meta.get_delete_permission()))
- self.assertTrue(self.client.login(username='deleteuser',
- password='secret'))
- response = self.client.get('/test_admin/admin/admin_views/plot/%s/delete/' % quote(1))
- self.assertContains(response, "your account doesn't have permission to delete the following types of objects")
- self.assertContains(response, "<li>plot details</li>")
- def test_protected(self):
- q = Question.objects.create(question="Why?")
- a1 = Answer.objects.create(question=q, answer="Because.")
- a2 = Answer.objects.create(question=q, answer="Yes.")
- response = self.client.get("/test_admin/admin/admin_views/question/%s/delete/" % quote(q.pk))
- self.assertContains(response, "would require deleting the following protected related objects")
- self.assertContains(response, '<li>Answer: <a href="/test_admin/admin/admin_views/answer/%s/">Because.</a></li>' % a1.pk)
- self.assertContains(response, '<li>Answer: <a href="/test_admin/admin/admin_views/answer/%s/">Yes.</a></li>' % a2.pk)
- def test_not_registered(self):
- should_contain = """<li>Secret hideout: underground bunker"""
- response = self.client.get('/test_admin/admin/admin_views/villain/%s/delete/' % quote(1))
- self.assertContains(response, should_contain, 1)
- def test_multiple_fkeys_to_same_model(self):
- """
- If a deleted object has two relationships from another model,
- both of those should be followed in looking for related
- objects to delete.
- """
- should_contain = """<li>Plot: <a href="/test_admin/admin/admin_views/plot/1/">World Domination</a>"""
- response = self.client.get('/test_admin/admin/admin_views/villain/%s/delete/' % quote(1))
- self.assertContains(response, should_contain)
- response = self.client.get('/test_admin/admin/admin_views/villain/%s/delete/' % quote(2))
- self.assertContains(response, should_contain)
- def test_multiple_fkeys_to_same_instance(self):
- """
- If a deleted object has two relationships pointing to it from
- another object, the other object should still only be listed
- once.
- """
- should_contain = """<li>Plot: <a href="/test_admin/admin/admin_views/plot/2/">World Peace</a></li>"""
- response = self.client.get('/test_admin/admin/admin_views/villain/%s/delete/' % quote(2))
- self.assertContains(response, should_contain, 1)
- def test_inheritance(self):
- """
- In the case of an inherited model, if either the child or
- parent-model instance is deleted, both instances are listed
- for deletion, as well as any relationships they have.
- """
- should_contain = [
- """<li>Villain: <a href="/test_admin/admin/admin_views/villain/3/">Bob</a>""",
- """<li>Super villain: <a href="/test_admin/admin/admin_views/supervillain/3/">Bob</a>""",
- """<li>Secret hideout: floating castle""",
- """<li>Super secret hideout: super floating castle!"""
- ]
- response = self.client.get('/test_admin/admin/admin_views/villain/%s/delete/' % quote(3))
- for should in should_contain:
- self.assertContains(response, should, 1)
- response = self.client.get('/test_admin/admin/admin_views/supervillain/%s/delete/' % quote(3))
- for should in should_contain:
- self.assertContains(response, should, 1)
- def test_generic_relations(self):
- """
- If a deleted object has GenericForeignKeys pointing to it,
- those objects should be listed for deletion.
- """
- plot = Plot.objects.get(pk=3)
- tag = FunkyTag.objects.create(content_object=plot, name='hott')
- should_contain = """<li>Funky tag: hott"""
- response = self.client.get('/test_admin/admin/admin_views/plot/%s/delete/' % quote(3))
- self.assertContains(response, should_contain)
- class AdminViewStringPrimaryKeyTest(TestCase):
- fixtures = ['admin-views-users.xml', 'string-primary-key.xml']
- def __init__(self, *args):
- super(AdminViewStringPrimaryKeyTest, self).__init__(*args)
- self.pk = """abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ 1234567890 -_.!~*'() ;/?:@&=+$, <>#%" {}|\^[]`"""
- def setUp(self):
- self.client.login(username='super', password='secret')
- content_type_pk = ContentType.objects.get_for_model(ModelWithStringPrimaryKey).pk
- LogEntry.objects.log_action(100, content_type_pk, self.pk, self.pk, 2, change_message='')
- def tearDown(self):
- self.client.logout()
- def test_get_history_view(self):
- "Retrieving the history for the object using urlencoded form of primary key should work"
- response = self.client.get('/test_admin/admin/admin_views/modelwithstringprimarykey/%s/history/' % quote(self.pk))
- self.assertContains(response, escape(self.pk))
- self.assertEqual(response.status_code, 200)
- def test_get_change_view(self):
- "Retrieving the object using urlencoded form of primary key should work"
- response = self.client.get('/test_admin/admin/admin_views/modelwithstringprimarykey/%s/' % quote(self.pk))
- self.assertContains(response, escape(self.pk))
- self.assertEqual(response.status_code, 200)
- def test_changelist_to_changeform_link(self):
- "The link from the changelist referring to the changeform of the object should be quoted"
- response = self.client.get('/test_admin/admin/admin_views/modelwithstringprimarykey/')
- should_contain = """<th><a href="%s/">%s</a></th></tr>""" % (quote(self.pk), escape(self.pk))
- self.assertContains(response, should_contain)
- def test_recentactions_link(self):
- "The link from the recent actions list referring to the changeform of the object should be quoted"
- response = self.client.get('/test_admin/admin/')
- should_contain = """<a href="admin_views/modelwithstringprimarykey/%s/">%s</a>""" % (quote(self.pk), escape(self.pk))
- self.assertContains(response, should_contain)
- def test_recentactions_without_content_type(self):
- "If a LogEntry is missing content_type it will not display it in span tag under the hyperlink."
- response = self.client.get('/test_admin/admin/')
- should_contain = """<a href="admin_views/modelwithstringprimarykey/%s/">%s</a>""" % (quote(self.pk), escape(self.pk))
- self.assertContains(response, should_contain)
- should_contain = "Model with string primary key"
- self.assertContains(response, should_contain)
- logentry = LogEntry.objects.get(content_type__name__iexact=should_contain)
-
-
-
- logentry.content_type = None
- logentry.save()
- counted_presence_before = response.content.count(should_contain)
- response = self.client.get('/test_admin/admin/')
- counted_presence_after = response.content.count(should_contain)
- self.assertEqual(counted_presence_before - 1,
- counted_presence_after)
- def test_deleteconfirmation_link(self):
- "The link from the delete confirmation page referring back to the changeform of the object should be quoted"
- response = self.client.get('/test_admin/admin/admin_views/modelwithstringprimarykey/%s/delete/' % quote(self.pk))
-
- should_contain = """/%s/">%s</a>""" % (iri_to_uri(quote(self.pk)), escape(self.pk))
- self.assertContains(response, should_contain)
- def test_url_conflicts_with_add(self):
- "A model with a primary key that ends with add should be visible"
- add_model = ModelWithStringPrimaryKey(id="i have something to add")
- add_model.save()
- response = self.client.get('/test_admin/admin/admin_views/modelwithstringprimarykey/%s/' % quote(add_model.pk))
- should_contain = """<h1>Change model with string primary key</h1>"""
- self.assertContains(response, should_contain)
- def test_url_conflicts_with_delete(self):
- "A model with a primary key that ends with delete should be visible"
- delete_model = ModelWithStringPrimaryKey(id="delete")
- delete_model.save()
- response = self.client.get('/test_admin/admin/admin_views/modelwithstringprimarykey/%s/' % quote(delete_model.pk))
- should_contain = """<h1>Change model with string primary key</h1>"""
- self.assertContains(response, should_contain)
- def test_url_conflicts_with_history(self):
- "A model with a primary key that ends with history should be visible"
- history_model = ModelWithStringPrimaryKey(id="history")
- history_model.save()
- response = self.client.get('/test_admin/admin/admin_views/modelwithstringprimarykey/%s/' % quote(history_model.pk))
- should_contain = """<h1>Change model with string primary key</h1>"""
- self.assertContains(response, should_contain)
- class SecureViewTests(TestCase):
- fixtures = ['admin-views-users.xml']
- def setUp(self):
-
- self.super_login = {
- LOGIN_FORM_KEY: 1,
- REDIRECT_FIELD_NAME: '/test_admin/admin/secure-view/',
- 'username': 'super',
- 'password': 'secret',
- }
- self.super_email_login = {
- LOGIN_FORM_KEY: 1,
- REDIRECT_FIELD_NAME: '/test_admin/admin/secure-view/',
- 'username': 'super@example.com',
- 'password': 'secret',
- }
- self.super_email_bad_login = {
- LOGIN_FORM_KEY: 1,
- REDIRECT_FIELD_NAME: '/test_admin/admin/secure-view/',
- 'username': 'super@example.com',
- 'password': 'notsecret',
- }
- self.adduser_login = {
- LOGIN_FORM_KEY: 1,
- REDIRECT_FIELD_NAME: '/test_admin/admin/secure-view/',
- 'username': 'adduser',
- 'password': 'secret',
- }
- self.changeuser_login = {
- LOGIN_FORM_KEY: 1,
- REDIRECT_FIELD_NAME: '/test_admin/admin/secure-view/',
- 'username': 'changeuser',
- 'password': 'secret',
- }
- self.deleteuser_login = {
- LOGIN_FORM_KEY: 1,
- REDIRECT_FIELD_NAME: '/test_admin/admin/secure-view/',
- 'username': 'deleteuser',
- 'password': 'secret',
- }
- self.joepublic_login = {
- LOGIN_FORM_KEY: 1,
- REDIRECT_FIELD_NAME: '/test_admin/admin/secure-view/',
- 'username': 'joepublic',
- 'password': 'secret',
- }
- def tearDown(self):
- self.client.logout()
- def test_secure_view_shows_login_if_not_logged_in(self):
- "Ensure that we see the login form"
- response = self.client.get('/test_admin/admin/secure-view/' )
- self.assertTemplateUsed(response, 'admin/login.html')
- def test_secure_view_login_successfully_redirects_to_original_url(self):
- request = self.client.get('/test_admin/admin/secure-view/')
- self.assertEqual(request.status_code, 200)
- query_string = 'the-answer=42'
- redirect_url = '/test_admin/admin/secure-view/?%s' % query_string
- new_next = {REDIRECT_FIELD_NAME: redirect_url}
- login = self.client.post('/test_admin/admin/secure-view/', dict(self.super_login, **new_next), QUERY_STRING=query_string)
- self.assertRedirects(login, redirect_url)
- def test_staff_member_required_decorator_works_as_per_admin_login(self):
- """
- Make sure only staff members can log in.
- Successful posts to the login page will redirect to the orignal url.
- Unsuccessfull attempts will continue to render the login page with
- a 200 status code.
- """
-
- request = self.client.get('/test_admin/admin/secure-view/')
- self.assertEqual(request.status_code, 200)
- login = self.client.post('/test_admin/admin/secure-view/', self.super_login)
- self.assertRedirects(login, '/test_admin/admin/secure-view/')
- self.assertFalse(login.context)
- self.client.get('/test_admin/admin/logout/')
-
- self.assertEqual(self.client.session.test_cookie_worked(), False)
-
- request = self.client.get('/test_admin/admin/secure-view/')
- self.assertEqual(request.status_code, 200)
- login = self.client.post('/test_admin/admin/secure-view/', self.super_email_login)
- self.assertContains(login, "Your e-mail address is not your username")
-
- login = self.client.post('/test_admin/admin/secure-view/', self.super_email_bad_login)
- self.assertContains(login, "Please enter a correct username and password.")
- new_user = User(username='jondoe', password='secret', email='super@example.com')
- new_user.save()
-
- login = self.client.post('/test_admin/admin/secure-view/', self.super_email_login)
- self.assertContains(login, "Please enter a correct username and password.")
-
- request = self.client.get('/test_admin/admin/secure-view/')
- self.assertEqual(request.status_code, 200)
- login = self.client.post('/test_admin/admin/secure-view/', self.adduser_login)
- self.assertRedirects(login, '/test_admin/admin/secure-view/')
- self.assertFalse(login.context)
- self.client.get('/test_admin/admin/logout/')
-
- request = self.client.get('/test_admin/admin/secure-view/')
- self.assertEqual(request.status_code, 200)
- login = self.client.post('/test_admin/admin/secure-view/', self.changeuser_login)
- self.assertRedirects(login, '/test_admin/admin/secure-view/')
- self.assertFalse(login.context)
- self.client.get('/test_admin/admin/logout/')
-
- request = self.client.get('/test_admin/admin/secure-view/')
- self.assertEqual(request.status_code, 200)
- login = self.client.post('/test_admin/admin/secure-view/', self.deleteuser_login)
- self.assertRedirects(login, '/test_admin/admin/secure-view/')
- self.assertFalse(login.context)
- self.client.get('/test_admin/admin/logout/')
-
- request = self.client.get('/test_admin/admin/secure-view/')
- self.assertEqual(request.status_code, 200)
- login = self.client.post('/test_admin/admin/secure-view/', self.joepublic_login)
- self.assertEqual(login.status_code, 200)
-
- self.assertContains(login, "Please enter a correct username and password.")
-
-
- login = self.client.login(username='joepublic', password='secret')
-
- self.client.get('/test_admin/admin/secure-view/')
- self.client.post('/test_admin/admin/secure-view/', self.super_login)
-
- self.assertEqual(self.client.session.test_cookie_worked(), False)
- def test_shortcut_view_only_available_to_staff(self):
- """
- Only admin users should be able to use the admin shortcut view.
- """
- user_ctype = ContentType.objects.get_for_model(User)
- user = User.objects.get(username='super')
- shortcut_url = "/test_admin/admin/r/%s/%s/" % (user_ctype.pk, user.pk)
-
- response = self.client.get(shortcut_url, follow=False)
- self.assertTemplateUsed(response, 'admin/login.html')
-
- self.client.login(username='super', password='secret')
- response = self.client.get(shortcut_url, follow=False)
-
- self.assertEqual(response.status_code, 302)
- self.assertEqual(response['Location'], 'http://example.com/users/super/')
- class AdminViewUnicodeTest(TestCase):
- fixtures = ['admin-views-unicode.xml']
- def setUp(self):
- self.client.login(username='super', password='secret')
- def tearDown(self):
- self.client.logout()
- def testUnicodeEdit(self):
- """
- A test to ensure that POST on edit_view handles non-ascii characters.
- """
- post_data = {
- "name": u"Test lærdommer",
-
- "chapter_set-TOTAL_FORMS": u"6",
- "chapter_set-INITIAL_FORMS": u"3",
- "chapter_set-MAX_NUM_FORMS": u"0",
- "chapter_set-0-id": u"1",
- "chapter_set-0-title": u"Norske bostaver æøå skaper problemer",
- "chapter_set-0-content": u"<p>Svært frustrerende med UnicodeDecodeError</p>",
- "chapter_set-1-id": u"2",
- "chapter_set-1-title": u"Kjærlighet.",
- "chapter_set-1-content": u"<p>La kjærligheten til de lidende seire.</p>",
- "chapter_set-2-id": u"3",
- "chapter_set-2-title": u"Need a title.",
- "chapter_set-2-content": u"<p>Newest content</p>",
- "chapter_set-3-id": u"",
- "chapter_set-3-title": u"",
- "chapter_set-3-content": u"",
- "chapter_set-4-id": u"",
- "chapter_set-4-title": u"",
- "chapter_set-4-content": u"",
- "chapter_set-5-id": u"",
- "chapter_set-5-title": u"",
- "chapter_set-5-content": u"",
- }
- response = self.client.post('/test_admin/admin/admin_views/book/1/', post_data)
- self.assertEqual(response.status_code, 302)
- def testUnicodeDelete(self):
- """
- Ensure that the delete_view handles non-ascii characters
- """
- delete_dict = {'post': 'yes'}
- response = self.client.get('/test_admin/admin/admin_views/book/1/delete/')
- self.assertEqual(response.status_code, 200)
- response = self.client.post('/test_admin/admin/admin_views/book/1/delete/', delete_dict)
- self.assertRedirects(response, '/test_admin/admin/admin_views/book/')
- class AdminViewListEditable(TestCase):
- fixtures = ['admin-views-users.xml', 'admin-views-person.xml']
- def setUp(self):
- self.client.login(username='super', password='secret')
- def tearDown(self):
- self.client.logout()
- def test_inheritance(self):
- Podcast.objects.create(name="This Week in Django",
- release_date=datetime.date.today())
- response = self.client.get('/test_admin/admin/admin_views/podcast/')
- self.assertEqual(response.status_code, 200)
- def test_inheritance_2(self):
- Vodcast.objects.create(name="This Week in Django", released=True)
- response = self.client.get('/test_admin/admin/admin_views/vodcast/')
- self.assertEqual(response.status_code, 200)
- def test_custom_pk(self):
- Language.objects.create(iso='en', name='English', english_name='English')
- response = self.client.get('/test_admin/admin/admin_views/language/')
- self.assertEqual(response.status_code, 200)
- def test_changelist_input_html(self):
- response = self.client.get('/test_admin/admin/admin_views/person/')
-
-
-
-
-
-
-
-
- self.assertEqual(response.content.count("<input"), 18)
-
- self.assertEqual(response.content.count("<select"), 4)
- def test_post_messages(self):
-
-
- data = {
- "form-TOTAL_FORMS": "3",
- "form-INITIAL_FORMS": "3",
- "form-MAX_NUM_FORMS": "0",
- "form-0-gender": "1",
- "form-0-id": "1",
- "form-1-gender": "2",
- "form-1-id": "2",
- "form-2-alive": "checked",
- "form-2-gender": "1",
- "form-2-id": "3",
- "_save": "Save",
- }
- response = self.client.post('/test_admin/admin/admin_views/person/',
- data, follow=True)
- self.assertEqual(len(response.context['messages']), 1)
- def test_post_submission(self):
- data = {
- "form-TOTAL_FORMS": "3",
- "form-INITIAL_FORMS": "3",
- "form-MAX_NUM_FORMS": "0",
- "form-0-gender": "1",
- "form-0-id": "1",
- "form-1-gender": "2",
- "form-1-id": "2",
- "form-2-alive": "checked",
- "form-2-gender": "1",
- "form-2-id": "3",
- "_save": "Save",
- }
- self.client.post('/test_admin/admin/admin_views/person/', data)
- self.assertEqual(Person.objects.get(name="John Mauchly").alive, False)
- self.assertEqual(Person.objects.get(name="Grace Hopper").gender, 2)
-
- data = {
- "form-TOTAL_FORMS": "2",
- "form-INITIAL_FORMS": "2",
- "form-MAX_NUM_FORMS": "0",
- "form-0-id": "1",
- "form-0-gender": "1",
- "form-0-alive": "checked",
- "form-1-id": "3",
- "form-1-gender": "1",
- "form-1-alive": "checked",
- "_save": "Save",
- }
- self.client.post('/test_admin/admin/admin_views/person/?gender__exact=1', data)
- self.assertEqual(Person.objects.get(name="John Mauchly").alive, True)
-
- data = {
- "form-TOTAL_FORMS": "1",
- "form-INITIAL_FORMS": "1",
- "form-MAX_NUM_FORMS": "0",
- "form-0-id": "1",
- "form-0-gender": "1",
- "_save": "Save",
- }
- self.client.post('/test_admin/admin/admin_views/person/?q=john', data)
- self.assertEqual(Person.objects.get(name="John Mauchly").alive, False)
- def test_non_field_errors(self):
- ''' Ensure that non field errors are displayed for each of the
- forms in the changelist's formset. Refs #13126.
- '''
- FoodDelivery.objects.create(reference='123', driver='bill', restaurant='thai')
- FoodDelivery.objects.create(reference='456', driver='bill', restaurant='india')
- FoodDelivery.objects.create(reference='789', driver='bill', restaurant='pizza')
- data = {
- "form-TOTAL_FORMS": "3",
- "form-INITIAL_FORMS": "3",
- "form-MAX_NUM_FORMS": "0",
- "form-0-id": "1",
- "form-0-reference": "123",
- "form-0-driver": "bill",
- "form-0-restaurant": "thai",
-
- "form-1-id": "2",
- "form-1-reference": "456",
- "form-1-driver": "bill",
- "form-1-restaurant": "thai",
- "form-2-id": "3",
- "form-2-reference": "789",
- "form-2-driver": "bill",
- "form-2-restaurant": "pizza",
- "_save": "Save",
- }
- response = self.client.post('/test_admin/admin/admin_views/fooddelivery/', data)
- self.assertContains(response, '<tr><td colspan="4"><ul class="errorlist"><li>Food delivery with this Driver and Restaurant already exists.</li></ul></td></tr>', 1)
- data = {
- "form-TOTAL_FORMS": "3",
- "form-INITIAL_FORMS": "3",
- "form-MAX_NUM_FORMS": "0",
- "form-0-id": "1",
- "form-0-reference": "123",
- "form-0-driver": "bill",
- "form-0-restaurant": "thai",
-
- "form-1-id": "2",
- "form-1-reference": "456",
- "form-1-driver": "bill",
- "form-1-restaurant": "thai",
-
- "form-2-id": "3",
- "form-2-reference": "789",
- "form-2-driver": "bill",
- "form-2-restaurant": "thai",
- "_save": "Save",
- }
- response = self.client.post('/test_admin/admin/admin_views/fooddelivery/', data)
- self.assertContains(response, '<tr><td colspan="4"><ul class="errorlist"><li>Food delivery with this Driver and Restaurant already exists.</li></ul></td></tr>', 2)
- def test_non_form_errors(self):
-
- data = {
- "form-TOTAL_FORMS": "1",
- "form-INITIAL_FORMS": "1",
- "form-MAX_NUM_FORMS": "0",
- "form-0-id": "2",
- "form-0-alive": "1",
- "form-0-gender": "2",
-
-
- "_save": "Save",
- }
- response = self.client.post('/test_admin/admin/admin_views/person/', data)
- self.assertContains(response, "Grace is not a Zombie")
- def test_non_form_errors_is_errorlist(self):
-
- data = {
- "form-TOTAL_FORMS": "1",
- "form-INITIAL_FORMS": "1",
- "form-MAX_NUM_FORMS": "0",
- "form-0-id": "2",
- "form-0-alive": "1",
- "form-0-gender": "2",
- "_save": "Save",
- }
- response = self.client.post('/test_admin/admin/admin_views/person/', data)
- non_form_errors = response.context['cl'].formset.non_form_errors()
- self.assertTrue(isinstance(non_form_errors, ErrorList))
- self.assertEqual(str(non_form_errors), str(ErrorList(["Grace is not a Zombie"])))
- def test_list_editable_ordering(self):
- collector = Collector.objects.create(id=1, name="Frederick Clegg")
- Category.objects.create(id=1, order=1, collector=collector)
- Category.objects.create(id=2, order=2, collector=collector)
- Category.objects.create(id=3, order=0, collector=collector)
- Category.objects.create(id=4, order=0, collector=collector)
-
- data = {
- "form-TOTAL_FORMS": "4",
- "form-INITIAL_FORMS": "4",
- "form-MAX_NUM_FORMS": "0",
- "form-0-order": "14",
- "form-0-id": "1",
- "form-0-collector": "1",
- "form-1-order": "13",
- "form-1-id": "2",
- "form-1-collector": "1",
- "form-2-order": "1",
- "form-2-id": "3",
- "form-2-collector": "1",
- "form-3-order": "0",
- "form-3-id": "4",
- "form-3-collector": "1",
-
-
- "_save": "Save",
- }
- response = self.client.post('/test_admin/admin/admin_views/category/', data)
-
- self.assertEqual(response.status_code, 302)
-
- self.assertEqual(Category.objects.get(id=1).order, 14)
- self.assertEqual(Category.objects.get(id=2).order, 13)
- self.assertEqual(Category.objects.get(id=3).order, 1)
- self.assertEqual(Category.objects.get(id=4).order, 0)
- def test_list_editable_action_submit(self):
-
-
- data = {
- "form-TOTAL_FORMS": "3",
- "form-INITIAL_FORMS": "3",
- "form-MAX_NUM_FORMS": "0",
- "form-0-gender": "1",
- "form-0-id": "1",
- "form-1-gender": "2",
- "form-1-id": "2",
- "form-2-alive": "checked",
- "form-2-gender": "1",
- "form-2-id": "3",
- "index": "0",
- "_selected_action": [u'3'],
- "action": [u'', u'delete_selected'],
- }
- self.client.post('/test_admin/admin/admin_views/person/', data)
- self.assertEqual(Person.objects.get(name="John Mauchly").alive, True)
- self.assertEqual(Person.objects.get(name="Grace Hopper").gender, 1)
- def test_list_editable_action_choices(self):
-
-
- data = {
- "form-TOTAL_FORMS": "3",
- "form-INITIAL_FORMS": "3",
- "form-MAX_NUM_FORMS": "0",
- "form-0-gender": "1",
- "form-0-id": "1",
- "form-1-gender": "2",
- "form-1-id": "2",
- "form-2-alive": "checked",
- "form-2-gender": "1",
- "form-2-id": "3",
- "_save": "Save",
- "_selected_action": [u'1'],
- "action": [u'', u'delete_selected'],
- }
- self.client.post('/test_admin/admin/admin_views/person/', data)
- self.assertEqual(Person.objects.get(name="John Mauchly").alive, False)
- self.assertEqual(Person.objects.get(name="Grace Hopper").gender, 2)
- def test_list_editable_popup(self):
- """
- Fields should not be list-editable in popups.
- """
- response = self.client.get('/test_admin/admin/admin_views/person/')
- self.assertNotEqual(response.context['cl'].list_editable, ())
- response = self.client.get('/test_admin/admin/admin_views/person/?%s' % IS_POPUP_VAR)
- self.assertEqual(response.context['cl'].list_editable, ())
- def test_pk_hidden_fields(self):
- """ Ensure that hidden pk fields aren't displayed in the table body and
- that their corresponding human-readable value is displayed instead.
- Note that the hidden pk fields are in fact be displayed but
- separately (not in the table), and only once.
- Refs #12475.
- """
- Story.objects.create(title='The adventures of Guido', content='Once upon a time in Djangoland...')
- Story.objects.create(title='Crouching Tiger, Hidden Python', content='The Python was sneaking into...')
- response = self.client.get('/test_admin/admin/admin_views/story/')
- self.assertContains(response, 'id="id_form-0-id"', 1)
- self.assertContains(response, 'id="id_form-1-id"', 1)
- self.assertContains(response, '<div class="hiddenfields">\n<input type="hidden" name="form-0-id" value="2" id="id_form-0-id" /><input type="hidden" name="form-1-id" value="1" id="id_form-1-id" />\n</div>')
- self.assertContains(response, '<td>1</td>', 1)
- self.assertContains(response, '<td>2</td>', 1)
- def test_pk_hidden_fields_with_list_display_links(self):
- """ Similarly as test_pk_hidden_fields, but when the hidden pk fields are
- referenced in list_display_links.
- Refs #12475.
- """
- OtherStory.objects.create(title='The adventures of Guido', content='Once upon a time in Djangoland...')
- OtherStory.objects.create(title='Crouching Tiger, Hidden Python', content='The Python was sneaking into...')
- response = self.client.get('/test_admin/admin/admin_views/otherstory/')
- self.assertContains(response, 'id="id_form-0-id"', 1)
- self.assertContains(response, 'id="id_form-1-id"', 1)
- self.assertContains(response, '<div class="hiddenfields">\n<input type="hidden" name="form-0-id" value="2" id="id_form-0-id" /><input type="hidden" name="form-1-id" value="1" id="id_form-1-id" />\n</div>')
- self.assertContains(response, '<th><a href="1/">1</a></th>', 1)
- self.assertContains(response, '<th><a href="2/">2</a></th>', 1)
- class AdminSearchTest(TestCase):
- fixtures = ['admin-views-users', 'multiple-child-classes',
- 'admin-views-person']
- def setUp(self):
- self.client.login(username='super', password='secret')
- def tearDown(self):
- self.client.logout()
- def test_search_on_sibling_models(self):
- "Check that a search that mentions sibling models"
- response = self.client.get('/test_admin/admin/admin_views/recommendation/?q=bar')
-
- self.assertContains(response, "\n1 recommendation\n")
- def test_with_fk_to_field(self):
- """Ensure that the to_field GET parameter is preserved when a search
- is performed. Refs #10918.
- """
- from django.contrib.admin.views.main import TO_FIELD_VAR
- response = self.client.get('/test_admin/admin/auth/user/?q=joe&%s=username' % TO_FIELD_VAR)
- self.assertContains(response, "\n1 user\n")
- self.assertContains(response, '<input type="hidden" name="t" value="username"/>')
- def test_exact_matches(self):
- response = self.client.get('/test_admin/admin/admin_views/recommendation/?q=bar')
-
- self.assertContains(response, "\n1 recommendation\n")
- response = self.client.get('/test_admin/admin/admin_views/recommendation/?q=ba')
-
- self.assertContains(response, "\n0 recommendations\n")
- def test_beginning_matches(self):
- response = self.client.get('/test_admin/admin/admin_views/person/?q=Gui')
-
- self.assertContains(response, "\n1 person\n")
- self.assertContains(response, "Guido")
- response = self.client.get('/test_admin/admin/admin_views/person/?q=uido')
-
- self.assertContains(response, "\n0 persons\n")
- self.assertNotContains(response, "Guido")
- class AdminInheritedInlinesTest(TestCase):
- fixtures = ['admin-views-users.xml',]
- def setUp(self):
- self.client.login(username='super', password='secret')
- def tearDown(self):
- self.client.logout()
- def testInline(self):
- "Ensure that inline models which inherit from a common parent are correctly handled by admin."
- foo_user = u"foo username"
- bar_user = u"bar username"
- name_re = re.compile('name="(.*?)"')
-
- response = self.client.get('/test_admin/admin/admin_views/persona/add/')
- names = name_re.findall(response.content)
-
- self.assertEqual(len(names), len(set(names)))
-
- post_data = {
- "name": u"Test Name",
-
- "accounts-TOTAL_FORMS": u"1",
- "accounts-INITIAL_FORMS": u"0",
- "accounts-MAX_NUM_FORMS": u"0",
- "accounts-0-username": foo_user,
- "accounts-2-TOTAL_FORMS": u"1",
- "accounts-2-INITIAL_FORMS": u"0",
- "accounts-2-MAX_NUM_FORMS": u"0",
- "accounts-2-0-username": bar_user,
- }
- response = self.client.post('/test_admin/admin/admin_views/persona/add/', post_data)
- self.assertEqual(response.status_code, 302)
- self.assertEqual(Persona.objects.count(), 1)
- self.assertEqual(FooAccount.objects.count(), 1)
- self.assertEqual(BarAccount.objects.count(), 1)
- self.assertEqual(FooAccount.objects.all()[0].username, foo_user)
- self.assertEqual(BarAccount.objects.all()[0].username, bar_user)
- self.assertEqual(Persona.objects.all()[0].accounts.count(), 2)
-
- response = self.client.get('/test_admin/admin/admin_views/persona/1/')
- names = name_re.findall(response.content)
-
- self.assertEqual(len(names), len(set(names)))
- post_data = {
- "name": u"Test Name",
- "accounts-TOTAL_FORMS": "2",
- "accounts-INITIAL_FORMS": u"1",
- "accounts-MAX_NUM_FORMS": u"0",
- "accounts-0-username": "%s-1" % foo_user,
- "accounts-0-account_ptr": "1",
- "accounts-0-persona": "1",
- "accounts-2-TOTAL_FORMS": u"2",
- "accounts-2-INITIAL_FORMS": u"1",
- "accounts-2-MAX_NUM_FORMS": u"0",
- "accounts-2-0-username": "%s-1" % bar_user,
- "accounts-2-0-account_ptr": "2",
- "accounts-2-0-persona": "1",
- }
- response = self.client.post('/test_admin/admin/admin_views/persona/1/', post_data)
- self.assertEqual(response.status_code, 302)
- self.assertEqual(Persona.objects.count(), 1)
- self.assertEqual(FooAccount.objects.count(), 1)
- self.assertEqual(BarAccount.objects.count(), 1)
- self.assertEqual(FooAccount.objects.all()[0].username, "%s-1" % foo_user)
- self.assertEqual(BarAccount.objects.all()[0].username, "%s-1" % bar_user)
- self.assertEqual(Persona.objects.all()[0].accounts.count(), 2)
- class AdminActionsTest(TestCase):
- fixtures = ['admin-views-users.xml', 'admin-views-actions.xml']
- def setUp(self):
- self.client.login(username='super', password='secret')
- def tearDown(self):
- self.client.logout()
- def test_model_admin_custom_action(self):
- "Tests a custom action defined in a ModelAdmin method"
- action_data = {
- ACTION_CHECKBOX_NAME: [1],
- 'action' : 'mail_admin',
- 'index': 0,
- }
- response = self.client.post('/test_admin/admin/admin_views/subscriber/', action_data)
- self.assertEqual(len(mail.outbox), 1)
- self.assertEqual(mail.outbox[0].subject, 'Greetings from a ModelAdmin action')
- def test_model_admin_default_delete_action(self):
- "Tests the default delete action defined as a ModelAdmin method"
- action_data = {
- ACTION_CHECKBOX_NAME: [1, 2],
- 'action' : 'delete_selected',
- 'index': 0,
- }
- delete_confirmation_data = {
- ACTION_CHECKBOX_NAME: [1, 2],
- 'action' : 'delete_selected',
- 'post': 'yes',
- }
- confirmation = self.client.post('/test_admin/admin/admin_views/subscriber/', action_data)
- self.assertContains(confirmation, "Are you sure you want to delete the selected subscribers")
- self.assertTrue(confirmation.content.count(ACTION_CHECKBOX_NAME) == 2)
- response = self.client.post('/test_admin/admin/admin_views/subscriber/', delete_confirmation_data)
- self.assertEqual(Subscriber.objects.count(), 0)
- def test_non_localized_pk(self):
- """If USE_THOUSAND_SEPARATOR is set, make sure that the ids for
- the objects selected for deletion are rendered without separators.
- Refs #14895.
- """
- self.old_USE_THOUSAND_SEPARATOR = settings.USE_THOUSAND_SEPARATOR
- self.old_USE_L10N = settings.USE_L10N
- settings.USE_THOUSAND_SEPARATOR = True
- settings.USE_L10N = True
- subscriber = Subscriber.objects.get(id=1)
- subscriber.id = 9999
- subscriber.save()
- action_data = {
- ACTION_CHECKBOX_NAME: [9999, 2],
- 'action' : 'delete_selected',
- 'index': 0,
- }
- response = self.client.post('/test_admin/admin/admin_views/subscriber/', action_data)
- self.assertTemplateUsed(response, 'admin/delete_selected_confirmation.html')
- self.assertTrue('value="9999"' in response.content and 'value="2"' in response.content)
- settings.USE_THOUSAND_SEPARATOR = self.old_USE_THOUSAND_SEPARATOR
- settings.USE_L10N = self.old_USE_L10N
- def test_model_admin_default_delete_action_protected(self):
- """
- Tests the default delete action defined as a ModelAdmin method in the
- case where some related objects are protected from deletion.
- """
- q1 = Question.objects.create(question="Why?")
- a1 = Answer.objects.create(question=q1, answer="Because.")
- a2 = Answer.objects.create(question=q1, answer="Yes.")
- q2 = Question.objects.create(question="Wherefore?")
- action_data = {
- ACTION_CHECKBOX_NAME: [q1.pk, q2.pk],
- 'action' : 'delete_selected',
- 'index': 0,
- }
- response = self.client.post("/test_admin/admin/admin_views/question/", action_data)
- self.assertContains(response, "would require deleting the following protected related objects")
- self.assertContains(response, '<li>Answer: <a href="/test_admin/admin/admin_views/answer/%s/">Because.</a></li>' % a1.pk)
- self.assertContains(response, '<li>Answer: <a href="/test_admin/admin/admin_views/answer/%s/">Yes.</a></li>' % a2.pk)
- def test_custom_function_mail_action(self):
- "Tests a custom action defined in a function"
- action_data = {
- ACTION_CHECKBOX_NAME: [1],
- 'action' : 'external_mail',
- 'index': 0,
- }
- response = self.client.post('/test_admin/admin/admin_views/externalsubscriber/', action_data)
- self.assertEqual(len(mail.outbox), 1)
- self.assertEqual(mail.outbox[0].subject, 'Greetings from a function action')
- def test_custom_function_action_with_redirect(self):
- "Tests a custom action defined in a function"
- action_data = {
- ACTION_CHECKBOX_NAME: [1],
- 'action' : 'redirect_to',
- 'index': 0,
- }
- response = self.client.post('/test_admin/admin/admin_views/externalsubscriber/', action_data)
- self.assertEqual(response.status_code, 302)
- def test_default_redirect(self):
- """
- Test that actions which don't return an HttpResponse are redirected to
- the same page, retaining the querystring (which may contain changelist
- information).
- """
- action_data = {
- ACTION_CHECKBOX_NAME: [1],
- 'action' : 'external_mail',
- 'index': 0,
- }
- url = '/test_admin/admin/admin_views/externalsubscriber/?ot=asc&o=1'
- response = self.client.post(url, action_data)
- self.assertRedirects(response, url)
- def test_model_without_action(self):
- "Tests a ModelAdmin without any action"
- response = self.client.get('/test_admin/admin/admin_views/oldsubscriber/')
- self.assertEqual(response.context["action_form"], None)
- self.assertTrue(
- '<input type="checkbox" class="action-select"' not in response.content,
- "Found an unexpected action toggle checkboxbox in response"
- )
- self.assertTrue('action-checkbox-column' not in response.content,
- "Found unexpected action-checkbox-column class in response")
- def test_model_without_action_still_has_jquery(self):
- "Tests that a ModelAdmin without any actions still gets jQuery included in page"
- response = self.client.get('/test_admin/admin/admin_views/oldsubscriber/')
- self.assertEqual(response.context["action_form"], None)
- self.assertTrue('jquery.min.js' in response.content,
- "jQuery missing from admin pages for model with no admin actions"
- )
- def test_action_column_class(self):
- "Tests that the checkbox column class is present in the response"
- response = self.client.get('/test_admin/admin/admin_views/subscriber/')
- self.assertNotEqual(response.context["action_form"], None)
- self.assertTrue('action-checkbox-column' in response.content,
- "Expected an action-checkbox-column in response")
- def test_multiple_actions_form(self):
- """
- Test that actions come from the form whose submit button was pressed (#10618).
- """
- action_data = {
- ACTION_CHECKBOX_NAME: [1],
-
- 'action': ['external_mail', 'delete_selected'],
-
- 'index': 0
- }
- response = self.client.post('/test_admin/admin/admin_views/externalsubscriber/', action_data)
-
- self.assertEqual(len(mail.outbox), 1)
- self.assertEqual(mail.outbox[0].subject, 'Greetings from a function action')
- def test_user_message_on_none_selected(self):
- """
- User should see a warning when 'Go' is pressed and no items are selected.
- """
- action_data = {
- ACTION_CHECKBOX_NAME: [],
- 'action' : 'delete_selected',
- 'index': 0,
- }
- response = self.client.post('/test_admin/admin/admin_views/subscriber/', action_data)
- msg = """Items must be selected in order to perform actions on them. No items have been changed."""
- self.assertContains(response, msg)
- self.assertEqual(Subscriber.objects.count(), 2)
- def test_user_message_on_no_action(self):
- """
- User should see a warning when 'Go' is pressed and no action is selected.
- """
- action_data = {
- ACTION_CHECKBOX_NAME: [1, 2],
- 'action' : '',
- 'index': 0,
- }
- response = self.client.post('/test_admin/admin/admin_views/subscriber/', action_data)
- msg = """No action selected."""
- self.assertContains(response, msg)
- self.assertEqual(Subscriber.objects.count(), 2)
- def test_selection_counter(self):
- """
- Check if the selection counter is there.
- """
- response = self.client.get('/test_admin/admin/admin_views/subscriber/')
- self.assertContains(response, '0 of 2 selected')
- def test_popup_actions(self):
- """ Actions should not be shown in popups. """
- response = self.client.get('/test_admin/admin/admin_views/subscriber/')
- self.assertNotEquals(response.context["action_form"], None)
- response = self.client.get(
- '/test_admin/admin/admin_views/subscriber/?%s' % IS_POPUP_VAR)
- self.assertEqual(response.context["action_form"], None)
- class TestCustomChangeList(TestCase):
- fixtures = ['admin-views-users.xml']
- urlbit = 'admin'
- def setUp(self):
- result = self.client.login(username='super', password='secret')
- self.assertEqual(result, True)
- def tearDown(self):
- self.client.logout()
- def test_custom_changelist(self):
- """
- Validate that a custom ChangeList class can be used (#9749)
- """
-
- post_data = {"name": u"First Gadget"}
- response = self.client.post('/test_admin/%s/admin_views/gadget/add/' % self.urlbit, post_data)
- self.assertEqual(response.status_code, 302)
-
- response = self.client.get('/test_admin/%s/admin_views/gadget/' % self.urlbit)
-
- response = self.client.get('/test_admin/%s/admin_views/gadget/' % self.urlbit)
- self.assertEqual(response.status_code, 200)
- self.assertNotContains(response, 'First Gadget')
- class TestInlineNotEditable(TestCase):
- fixtures = ['admin-views-users.xml']
- def setUp(self):
- result = self.client.login(username='super', password='secret')
- self.assertEqual(result, True)
- def tearDown(self):
- self.client.logout()
- def test(self):
- """
- InlineModelAdmin broken?
- """
- response = self.client.get('/test_admin/admin/admin_views/parent/add/')
- self.assertEqual(response.status_code, 200)
- class AdminCustomQuerysetTest(TestCase):
- fixtures = ['admin-views-users.xml']
- def setUp(self):
- self.client.login(username='super', password='secret')
- self.pks = [EmptyModel.objects.create().id for i in range(3)]
- def test_changelist_view(self):
- response = self.client.get('/test_admin/admin/admin_views/emptymodel/')
- for i in self.pks:
- if i > 1:
- self.assertContains(response, 'Primary key = %s' % i)
- else:
- self.assertNotContains(response, 'Primary key = %s' % i)
- def test_change_view(self):
- for i in self.pks:
- response = self.client.get('/test_admin/admin/admin_views/emptymodel/%s/' % i)
- if i > 1:
- self.assertEqual(response.status_code, 200)
- else:
- self.assertEqual(response.status_code, 404)
- def test_add_model_modeladmin_only_qs(self):
-
- p = Paper.objects.create(title=u"My Paper Title")
- self.assertEqual(Paper.objects.count(), 1)
- response = self.client.get('/test_admin/admin/admin_views/paper/%s/' % p.pk)
- self.assertEqual(response.status_code, 200)
- post_data = {
- "title": u"My Modified Paper Title",
- "_save": "Save",
- }
- response = self.client.post('/test_admin/admin/admin_views/paper/%s/' % p.pk,
- post_data, follow=True)
- self.assertEqual(response.status_code, 200)
-
- self.assertContains(response, '<li class="info">The paper "Paper_Deferred_author object" was changed successfully.</li>')
-
- cl = CoverLetter.objects.create(author=u"John Doe")
- self.assertEqual(CoverLetter.objects.count(), 1)
- response = self.client.get('/test_admin/admin/admin_views/coverletter/%s/' % cl.pk)
- self.assertEqual(response.status_code, 200)
- post_data = {
- "author": u"John Doe II",
- "_save": "Save",
- }
- response = self.client.post('/test_admin/admin/admin_views/coverletter/%s/' % cl.pk,
- post_data, follow=True)
- self.assertEqual(response.status_code, 200)
-
- self.assertContains(response, '<li class="info">The cover letter "John Doe II" was changed successfully.</li>')
- class AdminInlineFileUploadTest(TestCase):
- fixtures = ['admin-views-users.xml', 'admin-views-actions.xml']
- urlbit = 'admin'
- def setUp(self):
- self.client.login(username='super', password='secret')
-
-
-
- tdir = tempfile.gettempdir()
- file1 = tempfile.NamedTemporaryFile(suffix=".file1", dir=tdir)
- file1.write('a' * (2 ** 21))
- filename = file1.name
- file1.close()
- g = Gallery(name="Test Gallery")
- g.save()
- p = Picture(name="Test Picture", image=filename, gallery=g)
- p.save()
- def tearDown(self):
- self.client.logout()
- def test_inline_file_upload_edit_validation_error_post(self):
- """
- Test that inline file uploads correctly display prior data (#10002).
- """
- post_data = {
- "name": u"Test Gallery",
- "pictures-TOTAL_FORMS": u"2",
- "pictures-INITIAL_FORMS": u"1",
- "pictures-MAX_NUM_FORMS": u"0",
- "pictures-0-id": u"1",
- "pictures-0-gallery": u"1",
- "pictures-0-name": "Test Picture",
- "pictures-0-image": "",
- "pictures-1-id": "",
- "pictures-1-gallery": "1",
- "pictures-1-name": "Test Picture 2",
- "pictures-1-image": "",
- }
- response = self.client.post('/test_admin/%s/admin_views/gallery/1/' % self.urlbit, post_data)
- self.assertTrue(response._container[0].find("Currently:") > -1)
- class AdminInlineTests(TestCase):
- fixtures = ['admin-views-users.xml']
- def setUp(self):
- self.post_data = {
- "name": u"Test Name",
- "widget_set-TOTAL_FORMS": "3",
- "widget_set-INITIAL_FORMS": u"0",
- "widget_set-MAX_NUM_FORMS": u"0",
- "widget_set-0-id": "",
- "widget_set-0-owner": "1",
- "widget_set-0-name": "",
- "widget_set-1-id": "",
- "widget_set-1-owner": "1",
- "widget_set-1-name": "",
- "widget_set-2-id": "",
- "widget_set-2-owner": "1",
- "widget_set-2-name": "",
- "doohickey_set-TOTAL_FORMS": "3",
- "doohickey_set-INITIAL_FORMS": u"0",
- "doohickey_set-MAX_NUM_FORMS": u"0",
- "doohickey_set-0-owner": "1",
- "doohickey_set-0-code": "",
- "doohickey_set-0-name": "",
- "doohickey_set-1-owner": "1",
- "doohickey_set-1-code": "",
- "doohickey_set-1-name": "",
- "doohickey_set-2-owner": "1",
- "doohickey_set-2-code": "",
- "doohickey_set-2-name": "",
- "grommet_set-TOTAL_FORMS": "3",
- "grommet_set-INITIAL_FORMS": u"0",
- "grommet_set-MAX_NUM_FORMS": u"0",
- "grommet_set-0-code": "",
- "grommet_set-0-owner": "1",
- "grommet_set-0-name": "",
- "grommet_set-1-code": "",
- "grommet_set-1-owner": "1",
- "grommet_set-1-name": "",
- "grommet_set-2-code": "",
- "grommet_set-2-owner": "1",
- "grommet_set-2-name": "",
- "whatsit_set-TOTAL_FORMS": "3",
- "whatsit_set-INITIAL_FORMS": u"0",
- "whatsit_set-MAX_NUM_FORMS": u"0",
- "whatsit_set-0-owner": "1",
- "whatsit_set-0-index": "",
- "whatsit_set-0-name": "",
- "whatsit_set-1-owner": "1",
- "whatsit_set-1-index": "",
- "whatsit_set-1-name": "",
- "whatsit_set-2-owner": "1",
- "whatsit_set-2-index": "",
- "whatsit_set-2-name": "",
- "fancydoodad_set-TOTAL_FORMS": "3",
- "fancydoodad_set-INITIAL_FORMS": u"0",
- "fancydoodad_set-MAX_NUM_FORMS": u"0",
- "fancydoodad_set-0-doodad_ptr": "",
- "fancydoodad_set-0-owner": "1",
- "fancydoodad_set-0-name": "",
- "fancydoodad_set-0-expensive": "on",
- "fancydoodad_set-1-doodad_ptr": "",
- "fancydoodad_set-1-owner": "1",
- "fancydoodad_set-1-name": "",
- "fancydoodad_set-1-expensive": "on",
- "fancydoodad_set-2-doodad_ptr": "",
- "fancydoodad_set-2-owner": "1",
- "fancydoodad_set-2-name": "",
- "fancydoodad_set-2-expensive": "on",
- "category_set-TOTAL_FORMS": "3",
- "category_set-INITIAL_FORMS": "0",
- "category_set-MAX_NUM_FORMS": "0",
- "category_set-0-order": "",
- "category_set-0-id": "",
- "category_set-0-collector": "1",
- "category_set-1-order": "",
- "category_set-1-id": "",
- "category_set-1-collector": "1",
- "category_set-2-order": "",
- "category_set-2-id": "",
- "category_set-2-collector": "1",
- }
- result = self.client.login(username='super', password='secret')
- self.assertEqual(result, True)
- self.collector = Collector(pk=1,name='John Fowles')
- self.collector.save()
- def tearDown(self):
- self.client.logout()
- def test_simple_inline(self):
- "A simple model can be saved as inlines"
-
- self.post_data['widget_set-0-name'] = "Widget 1"
- response = self.client.post('/test_admin/admin/admin_views/collector/1/', self.post_data)
- self.assertEqual(response.status_code, 302)
- self.assertEqual(Widget.objects.count(), 1)
- self.assertEqual(Widget.objects.all()[0].name, "Widget 1")
-
- response = self.client.get('/test_admin/admin/admin_views/collector/1/')
- self.assertContains(response, 'name="widget_set-0-id"')
-
- self.post_data['widget_set-INITIAL_FORMS'] = "1"
- self.post_data['widget_set-0-id'] = "1"
- self.post_data['widget_set-0-name'] = "Widget 1"
- response = self.client.post('/test_admin/admin/admin_views/collector/1/', self.post_data)
- self.assertEqual(response.status_code, 302)
- self.assertEqual(Widget.objects.count(), 1)
- self.assertEqual(Widget.objects.all()[0].name, "Widget 1")
-
- self.post_data['widget_set-INITIAL_FORMS'] = "1"
- self.post_data['widget_set-0-id'] = "1"
- self.post_data['widget_set-0-name'] = "Widget 1 Updated"
- response = self.client.post('/test_admin/admin/admin_views/collector/1/', self.post_data)
- self.assertEqual(response.status_code, 302)
- self.assertEqual(Widget.objects.count(), 1)
- self.assertEqual(Widget.objects.all()[0].name, "Widget 1 Updated")
- def test_explicit_autofield_inline(self):
- "A model with an explicit autofield primary key can be saved as inlines. Regression for #8093"
-
- self.post_data['grommet_set-0-name'] = "Grommet 1"
- response = self.client.post('/test_admin/admin/admin_views/collector/1/', self.post_data)
- self.assertEqual(response.status_code, 302)
- self.assertEqual(Grommet.objects.count(), 1)
- self.assertEqual(Grommet.objects.all()[0].name, "Grommet 1")
-
- response = self.client.get('/test_admin/admin/admin_views/collector/1/')
- self.assertContains(response, 'name="grommet_set-0-code"')
-
- self.post_data['grommet_set-INITIAL_FORMS'] = "1"
- self.post_data['grommet_set-0-code'] = "1"
- self.post_data['grommet_set-0-name'] = "Grommet 1"
- response = self.client.post('/test_admin/admin/admin_views/collector/1/', self.post_data)
- self.assertEqual(response.status_code, 302)
- self.assertEqual(Grommet.objects.count(), 1)
- self.assertEqual(Grommet.objects.all()[0].name, "Grommet 1")
-
- self.post_data['grommet_set-INITIAL_FORMS'] = "1"
- self.post_data['grommet_set-0-code'] = "1"
- self.post_data['grommet_set-0-name'] = "Grommet 1 Updated"
- response = self.client.post('/test_admin/admin/admin_views/collector/1/', self.post_data)
- self.assertEqual(response.status_code, 302)
- self.assertEqual(Grommet.objects.count(), 1)
- self.assertEqual(Grommet.objects.all()[0].name, "Grommet 1 Updated")
- def test_char_pk_inline(self):
- "A model with a character PK can be saved as inlines. Regression for #10992"
-
- self.post_data['doohickey_set-0-code'] = "DH1"
- self.post_data['doohickey_set-0-name'] = "Doohickey 1"
- response = self.client.post('/test_admin/admin/admin_views/collector/1/', self.post_data)
- self.assertEqual(response.status_code, 302)
- self.assertEqual(DooHickey.objects.count(), 1)
- self.assertEqual(DooHickey.objects.all()[0].name, "Doohickey 1")
-
- response = self.client.get('/test_admin/admin/admin_views/collector/1/')
- self.assertContains(response, 'name="doohickey_set-0-code"')
-
- self.post_data['doohickey_set-INITIAL_FORMS'] = "1"
- self.post_data['doohickey_set-0-code'] = "DH1"
- self.post_data['doohickey_set-0-name'] = "Doohickey 1"
- response = self.client.post('/test_admin/admin/admin_views/collector/1/', self.post_data)
- self.assertEqual(response.status_code, 302)
- self.assertEqual(DooHickey.objects.count(), 1)
- self.assertEqual(DooHickey.objects.all()[0].name, "Doohickey 1")
-
- self.post_data['doohickey_set-INITIAL_FORMS'] = "1"
- self.post_data['doohickey_set-0-code'] = "DH1"
- self.post_data['doohickey_set-0-name'] = "Doohickey 1 Updated"
- response = self.client.post('/test_admin/admin/admin_views/collector/1/', self.post_data)
- self.assertEqual(response.status_code, 302)
- self.assertEqual(DooHickey.objects.count(), 1)
- self.assertEqual(DooHickey.objects.all()[0].name, "Doohickey 1 Updated")
- def test_integer_pk_inline(self):
- "A model with an integer PK can be saved as inlines. Regression for #10992"
-
- self.post_data['whatsit_set-0-index'] = "42"
- self.post_data['whatsit_set-0-name'] = "Whatsit 1"
- response = self.client.post('/test_admin/admin/admin_views/collector/1/', self.post_data)
- self.assertEqual(response.status_code, 302)
- self.assertEqual(Whatsit.objects.count(), 1)
- self.assertEqual(Whatsit.objects.all()[0].name, "Whatsit 1")
-
- response = self.client.get('/test_admin/admin/admin_views/collector/1/')
- self.assertContains(response, 'name="whatsit_set-0-index"')
-
- self.post_data['whatsit_set-INITIAL_FORMS'] = "1"
- self.post_data['whatsit_set-0-index'] = "42"
- self.post_data['whatsit_set-0-name'] = "Whatsit 1"
- response = self.client.post('/test_admin/admin/admin_views/collector/1/', self.post_data)
- self.assertEqual(response.status_code, 302)
- self.assertEqual(Whatsit.objects.count(), 1)
- self.assertEqual(Whatsit.objects.all()[0].name, "Whatsit 1")
-
- self.post_data['whatsit_set-INITIAL_FORMS'] = "1"
- self.post_data['whatsit_set-0-index'] = "42"
- self.post_data['whatsit_set-0-name'] = "Whatsit 1 Updated"
- response = self.client.post('/test_admin/admin/admin_views/collector/1/', self.post_data)
- self.assertEqual(response.status_code, 302)
- self.assertEqual(Whatsit.objects.count(), 1)
- self.assertEqual(Whatsit.objects.all()[0].name, "Whatsit 1 Updated")
- def test_inherited_inline(self):
- "An inherited model can be saved as inlines. Regression for #11042"
-
- self.post_data['fancydoodad_set-0-name'] = "Fancy Doodad 1"
- response = self.client.post('/test_admin/admin/admin_views/collector/1/', self.post_data)
- self.assertEqual(response.status_code, 302)
- self.assertEqual(FancyDoodad.objects.count(), 1)
- self.assertEqual(FancyDoodad.objects.all()[0].name, "Fancy Doodad 1")
-
- response = self.client.get('/test_admin/admin/admin_views/collector/1/')
- self.assertContains(response, 'name="fancydoodad_set-0-doodad_ptr"')
-
- self.post_data['fancydoodad_set-INITIAL_FORMS'] = "1"
- self.post_data['fancydoodad_set-0-doodad_ptr'] = "1"
- self.post_data['fancydoodad_set-0-name'] = "Fancy Doodad 1"
- response = self.client.post('/test_admin/admin/admin_views/collector/1/', self.post_data)
- self.assertEqual(response.status_code, 302)
- self.assertEqual(FancyDoodad.objects.count(), 1)
- self.assertEqual(FancyDoodad.objects.all()[0].name, "Fancy Doodad 1")
-
- self.post_data['fancydoodad_set-INITIAL_FORMS'] = "1"
- self.post_data['fancydoodad_set-0-doodad_ptr'] = "1"
- self.post_data['fancydoodad_set-0-name'] = "Fancy Doodad 1 Updated"
- response = self.client.post('/test_admin/admin/admin_views/collector/1/', self.post_data)
- self.assertEqual(response.status_code, 302)
- self.assertEqual(FancyDoodad.objects.count(), 1)
- self.assertEqual(FancyDoodad.objects.all()[0].name, "Fancy Doodad 1 Updated")
- def test_ordered_inline(self):
- """Check that an inline with an editable ordering fields is
- updated correctly. Regression for #10922"""
-
- Category.objects.create(id=1, order=1, collector=self.collector)
- Category.objects.create(id=2, order=2, collector=self.collector)
- Category.objects.create(id=3, order=0, collector=self.collector)
- Category.objects.create(id=4, order=0, collector=self.collector)
-
- self.post_data.update({
- "name": "Frederick Clegg",
- "category_set-TOTAL_FORMS": "7",
- "category_set-INITIAL_FORMS": "4",
- "category_set-MAX_NUM_FORMS": "0",
- "category_set-0-order": "14",
- "category_set-0-id": "1",
- "category_set-0-collector": "1",
- "category_set-1-order": "13",
- "category_set-1-id": "2",
- "category_set-1-collector": "1",
- "category_set-2-order": "1",
- "category_set-2-id": "3",
- "category_set-2-collector": "1",
- "category_set-3-order": "0",
- "category_set-3-id": "4",
- "category_set-3-collector": "1",
- "category_set-4-order": "",
- "category_set-4-id": "",
- "category_set-4-collector": "1",
- "category_set-5-order": "",
- "category_set-5-id": "",
- "category_set-5-collector": "1",
- "category_set-6-order": "",
- "category_set-6-id": "",
- "category_set-6-collector": "1",
- })
- response = self.client.post('/test_admin/admin/admin_views/collector/1/', self.post_data)
-
- self.assertEqual(response.status_code, 302)
-
- self.assertEqual(self.collector.category_set.count(), 4)
- self.assertEqual(Category.objects.get(id=1).order, 14)
- self.assertEqual(Category.objects.get(id=2).order, 13)
- self.assertEqual(Category.objects.get(id=3).order, 1)
- self.assertEqual(Category.objects.get(id=4).order, 0)
- class NeverCacheTests(TestCase):
- fixtures = ['admin-views-users.xml', 'admin-views-colors.xml', 'admin-views-fabrics.xml']
- def setUp(self):
- self.client.login(username='super', password='secret')
- def tearDown(self):
- self.client.logout()
- def testAdminIndex(self):
- "Check the never-cache status of the main index"
- response = self.client.get('/test_admin/admin/')
- self.assertEqual(get_max_age(response), 0)
- def testAppIndex(self):
- "Check the never-cache status of an application index"
- response = self.client.get('/test_admin/admin/admin_views/')
- self.assertEqual(get_max_age(response), 0)
- def testModelIndex(self):
- "Check the never-cache status of a model index"
- response = self.client.get('/test_admin/admin/admin_views/fabric/')
- self.assertEqual(get_max_age(response), 0)
- def testModelAdd(self):
- "Check the never-cache status of a model add page"
- response = self.client.get('/test_admin/admin/admin_views/fabric/add/')
- self.assertEqual(get_max_age(response), 0)
- def testModelView(self):
- "Check the never-cache status of a model edit page"
- response = self.client.get('/test_admin/admin/admin_views/section/1/')
- self.assertEqual(get_max_age(response), 0)
- def testModelHistory(self):
- "Check the never-cache status of a model history page"
- response = self.client.get('/test_admin/admin/admin_views/section/1/history/')
- self.assertEqual(get_max_age(response), 0)
- def testModelDelete(self):
- "Check the never-cache status of a model delete page"
- response = self.client.get('/test_admin/admin/admin_views/section/1/delete/')
- self.assertEqual(get_max_age(response), 0)
- def testLogin(self):
- "Check the never-cache status of login views"
- self.client.logout()
- response = self.client.get('/test_admin/admin/')
- self.assertEqual(get_max_age(response), 0)
- def testLogout(self):
- "Check the never-cache status of logout view"
- response = self.client.get('/test_admin/admin/logout/')
- self.assertEqual(get_max_age(response), 0)
- def testPasswordChange(self):
- "Check the never-cache status of the password change view"
- self.client.logout()
- response = self.client.get('/test_admin/password_change/')
- self.assertEqual(get_max_age(response), None)
- def testPasswordChangeDone(self):
- "Check the never-cache status of the password change done view"
- response = self.client.get('/test_admin/admin/password_change/done/')
- self.assertEqual(get_max_age(response), None)
- def testJsi18n(self):
- "Check the never-cache status of the Javascript i18n view"
- response = self.client.get('/test_admin/admin/jsi18n/')
- self.assertEqual(get_max_age(response), None)
- class ReadonlyTest(TestCase):
- fixtures = ['admin-views-users.xml']
- def setUp(self):
- self.client.login(username='super', password='secret')
- def tearDown(self):
- self.client.logout()
- def test_readonly_get(self):
- response = self.client.get('/test_admin/admin/admin_views/post/add/')
- self.assertEqual(response.status_code, 200)
- self.assertNotContains(response, 'name="posted"')
-
-
- self.assertEqual(response.content.count("<input"), 14)
- self.assertContains(response, formats.localize(datetime.date.today()))
- self.assertContains(response,
- "<label>Awesomeness level:</label>")
- self.assertContains(response, "Very awesome.")
- self.assertContains(response, "Unkown coolness.")
- self.assertContains(response, "foo")
- self.assertContains(response,
- formats.localize(datetime.date.today() - datetime.timedelta(days=7))
- )
- self.assertContains(response, '<div class="form-row coolness">')
- self.assertContains(response, '<div class="form-row awesomeness_level">')
- self.assertContains(response, '<div class="form-row posted">')
- self.assertContains(response, '<div class="form-row value">')
- self.assertContains(response, '<div class="form-row ">')
- self.assertContains(response, '<p class="help">', 3)
- self.assertContains(response, '<p class="help">Some help text for the title (with unicode ŠĐĆŽćžšđ)</p>')
- self.assertContains(response, '<p class="help">Some help text for the content (with unicode ŠĐĆŽćžšđ)</p>')
- self.assertContains(response, '<p class="help">Some help text for the date (with unicode ŠĐĆŽćžšđ)</p>')
- p = Post.objects.create(title="I worked on readonly_fields", content="Its good stuff")
- response = self.client.get('/test_admin/admin/admin_views/post/%d/' % p.pk)
- self.assertContains(response, "%d amount of cool" % p.pk)
- def test_readonly_post(self):
- data = {
- "title": "Django Got Readonly Fields",
- "content": "This is an incredible development.",
- "link_set-TOTAL_FORMS": "1",
- "link_set-INITIAL_FORMS": "0",
- "link_set-MAX_NUM_FORMS": "0",
- }
- response = self.client.post('/test_admin/admin/admin_views/post/add/', data)
- self.assertEqual(response.status_code, 302)
- self.assertEqual(Post.objects.count(), 1)
- p = Post.objects.get()
- self.assertEqual(p.posted, datetime.date.today())
- data["posted"] = "10-8-1990"
- response = self.client.post('/test_admin/admin/admin_views/post/add/', data)
- self.assertEqual(response.status_code, 302)
- self.assertEqual(Post.objects.count(), 2)
- p = Post.objects.order_by('-id')[0]
- self.assertEqual(p.posted, datetime.date.today())
- def test_readonly_manytomany(self):
- "Regression test for #13004"
- response = self.client.get('/test_admin/admin/admin_views/pizza/add/')
- self.assertEqual(response.status_code, 200)
- class RawIdFieldsTest(TestCase):
- fixtures = ['admin-views-users.xml']
- def setUp(self):
- self.client.login(username='super', password='secret')
- def tearDown(self):
- self.client.logout()
- def test_limit_choices_to(self):
- """Regression test for 14880"""
-
- actor = Actor.objects.create(name="Palin", age=27)
- inquisition1 = Inquisition.objects.create(expected=True,
- leader=actor,
- country="England")
- inquisition2 = Inquisition.objects.create(expected=False,
- leader=actor,
- country="Spain")
- response = self.client.get('/test_admin/admin/admin_views/sketch/add/')
-
- m = re.search(r'<a href="([^"]*)"[^>]* id="lookup_id_inquisition"', response.content)
- self.assertTrue(m)
- popup_url = m.groups()[0].replace("&", "&")
-
- popup_url = urlparse.urljoin(response.request['PATH_INFO'], popup_url)
-
- response2 = self.client.get(popup_url)
- self.assertContains(response2, "Spain")
- self.assertNotContains(response2, "England")
- class UserAdminTest(TestCase):
- """
- Tests user CRUD functionality.
- """
- fixtures = ['admin-views-users.xml']
- def setUp(self):
- self.client.login(username='super', password='secret')
- def tearDown(self):
- self.client.logout()
- def test_save_button(self):
- user_count = User.objects.count()
- response = self.client.post('/test_admin/admin/auth/user/add/', {
- 'username': 'newuser',
- 'password1': 'newpassword',
- 'password2': 'newpassword',
- })
- new_user = User.objects.order_by('-id')[0]
- self.assertRedirects(response, '/test_admin/admin/auth/user/%s/' % new_user.pk)
- self.assertEqual(User.objects.count(), user_count + 1)
- self.assertNotEqual(new_user.password, UNUSABLE_PASSWORD)
- def test_save_continue_editing_button(self):
- user_count = User.objects.count()
- response = self.client.post('/test_admin/admin/auth/user/add/', {
- 'username': 'newuser',
- 'password1': 'newpassword',
- 'password2': 'newpassword',
- '_continue': '1',
- })
- new_user = User.objects.order_by('-id')[0]
- self.assertRedirects(response, '/test_admin/admin/auth/user/%s/' % new_user.pk)
- self.assertEqual(User.objects.count(), user_count + 1)
- self.assertNotEqual(new_user.password, UNUSABLE_PASSWORD)
- def test_password_mismatch(self):
- response = self.client.post('/test_admin/admin/auth/user/add/', {
- 'username': 'newuser',
- 'password1': 'newpassword',
- 'password2': 'mismatch',
- })
- self.assertEqual(response.status_code, 200)
- adminform = response.context['adminform']
- self.assertTrue('password' not in adminform.form.errors)
- self.assertEqual(adminform.form.errors['password2'],
- [u"The two password fields didn't match."])
- def test_user_fk_popup(self):
- """Quick user addition in a FK popup shouldn't invoke view for further user customization"""
- response = self.client.get('/test_admin/admin/admin_views/album/add/')
- self.assertEqual(response.status_code, 200)
- self.assertContains(response, '/test_admin/admin/auth/user/add')
- self.assertContains(response, 'class="add-another" id="add_id_owner" onclick="return showAddAnotherPopup(this);"')
- response = self.client.get('/test_admin/admin/auth/user/add/?_popup=1')
- self.assertEqual(response.status_code, 200)
- self.assertNotContains(response, 'name="_continue"')
- self.assertNotContains(response, 'name="_addanother"')
- data = {
- 'username': 'newuser',
- 'password1': 'newpassword',
- 'password2': 'newpassword',
- '_popup': '1',
- '_save': '1',
- }
- response = self.client.post('/test_admin/admin/auth/user/add/?_popup=1', data, follow=True)
- self.assertEqual(response.status_code, 200)
- self.assertContains(response, 'dismissAddAnotherPopup')
- def test_save_add_another_button(self):
- user_count = User.objects.count()
- response = self.client.post('/test_admin/admin/auth/user/add/', {
- 'username': 'newuser',
- 'password1': 'newpassword',
- 'password2': 'newpassword',
- '_addanother': '1',
- })
- new_user = User.objects.order_by('-id')[0]
- self.assertRedirects(response, '/test_admin/admin/auth/user/add/')
- self.assertEqual(User.objects.count(), user_count + 1)
- self.assertNotEqual(new_user.password, UNUSABLE_PASSWORD)
- try:
- import docutils
- except ImportError:
- docutils = None
- class AdminDocsTest(TestCase):
- fixtures = ['admin-views-users.xml']
- def setUp(self):
- self.client.login(username='super', password='secret')
- def tearDown(self):
- self.client.logout()
- def test_tags(self):
- response = self.client.get('/test_admin/admin/doc/tags/')
-
- self.assertContains(response, "<h2>Built-in tags</h2>", count=2)
-
- self.assertContains(response, '<h3 id="built_in-autoescape">autoescape</h3>')
- self.assertContains(response, '<li><a href="#built_in-autoescape">autoescape</a></li>')
-
- self.assertContains(response, '<h3 id="flatpages-get_flatpages">get_flatpages</h3>')
- self.assertContains(response, '<li><a href="#flatpages-get_flatpages">get_flatpages</a></li>')
-
- self.assertContains(response, "<h2>admin_list</h2>", count=2)
-
- self.assertContains(response, '<h3 id="admin_list-admin_actions">admin_actions</h3>')
- self.assertContains(response, '<li><a href="#admin_list-admin_actions">admin_actions</a></li>')
- def test_filters(self):
- response = self.client.get('/test_admin/admin/doc/filters/')
-
- self.assertContains(response, "<h2>Built-in filters</h2>", count=2)
-
- self.assertContains(response, '<h3 id="built_in-add">add</h3>')
- self.assertContains(response, '<li><a href="#built_in-add">add</a></li>')
- AdminDocsTest = unittest.skipUnless(docutils, "no docutils installed.")(AdminDocsTest)
- class ValidXHTMLTests(TestCase):
- fixtures = ['admin-views-users.xml']
- urlbit = 'admin'
- def setUp(self):
- self._context_processors = None
- self._use_i18n, settings.USE_I18N = settings.USE_I18N, False
- if 'django.core.context_processors.i18n' in settings.TEMPLATE_CONTEXT_PROCESSORS:
- self._context_processors = settings.TEMPLATE_CONTEXT_PROCESSORS
- cp = list(settings.TEMPLATE_CONTEXT_PROCESSORS)
- cp.remove('django.core.context_processors.i18n')
- settings.TEMPLATE_CONTEXT_PROCESSORS = tuple(cp)
-
- django.template.context._standard_context_processors = None
- self.client.login(username='super', password='secret')
- def tearDown(self):
- self.client.logout()
- if self._context_processors is not None:
- settings.TEMPLATE_CONTEXT_PROCESSORS = self._context_processors
-
- django.template.context._standard_context_processors = None
- settings.USE_I18N = self._use_i18n
- def testLangNamePresent(self):
- response = self.client.get('/test_admin/%s/admin_views/' % self.urlbit)
- self.assertFalse(' lang=""' in response.content)
- self.assertFalse(' xml:lang=""' in response.content)
- class DateHierarchyTests(TestCase):
- fixtures = ['admin-views-users.xml']
- def setUp(self):
- self.client.login(username='super', password='secret')
- self.old_USE_THOUSAND_SEPARATOR = settings.USE_THOUSAND_SEPARATOR
- self.old_USE_L10N = settings.USE_L10N
- settings.USE_THOUSAND_SEPARATOR = True
- settings.USE_L10N = True
- def tearDown(self):
- settings.USE_THOUSAND_SEPARATOR = self.old_USE_THOUSAND_SEPARATOR
- settings.USE_L10N = self.old_USE_L10N
- formats.reset_format_cache()
- def assert_non_localized_year(self, response, year):
- """Ensure that the year is not localized with
- USE_THOUSAND_SEPARATOR. Refs #15234.
- """
- self.assertNotContains(response, formats.number_format(year))
- def assert_contains_year_link(self, response, date):
- self.assertContains(response, '?release_date__year=%d"' % (date.year,))
- def assert_contains_month_link(self, response, date):
- self.assertContains(
- response, '?release_date__year=%d&release_date__month=%d"' % (
- date.year, date.month))
- def assert_contains_day_link(self, response, date):
- self.assertContains(
- response, '?release_date__year=%d&'
- 'release_date__month=%d&release_date__day=%d"' % (
- date.year, date.month, date.day))
- def test_empty(self):
- """
- Ensure that no date hierarchy links display with empty changelist.
- """
- response = self.client.get(
- reverse('admin:admin_views_podcast_changelist'))
- self.assertNotContains(response, 'release_date__year=')
- self.assertNotContains(response, 'release_date__month=')
- self.assertNotContains(response, 'release_date__day=')
- def test_single(self):
- """
- Ensure that single day-level date hierarchy appears for single object.
- """
- DATE = datetime.date(2000, 6, 30)
- Podcast.objects.create(release_date=DATE)
- url = reverse('admin:admin_views_podcast_changelist')
- response = self.client.get(url)
- self.assert_contains_day_link(response, DATE)
- self.assert_non_localized_year(response, 2000)
- def test_within_month(self):
- """
- Ensure that day-level links appear for changelist within single month.
- """
- DATES = (datetime.date(2000, 6, 30),
- datetime.date(2000, 6, 15),
- datetime.date(2000, 6, 3))
- for date in DATES:
- Podcast.objects.create(release_date=date)
- url = reverse('admin:admin_views_podcast_changelist')
- response = self.client.get(url)
- for date in DATES:
- self.assert_contains_day_link(response, date)
- self.assert_non_localized_year(response, 2000)
- def test_within_year(self):
- """
- Ensure that month-level links appear for changelist within single year.
- """
- DATES = (datetime.date(2000, 1, 30),
- datetime.date(2000, 3, 15),
- datetime.date(2000, 5, 3))
- for date in DATES:
- Podcast.objects.create(release_date=date)
- url = reverse('admin:admin_views_podcast_changelist')
- response = self.client.get(url)
-
- self.assertNotContains(response, 'release_date__day=')
- for date in DATES:
- self.assert_contains_month_link(response, date)
- self.assert_non_localized_year(response, 2000)
- def test_multiple_years(self):
- """
- Ensure that year-level links appear for year-spanning changelist.
- """
- DATES = (datetime.date(2001, 1, 30),
- datetime.date(2003, 3, 15),
- datetime.date(2005, 5, 3))
- for date in DATES:
- Podcast.objects.create(release_date=date)
- response = self.client.get(
- reverse('admin:admin_views_podcast_changelist'))
-
- self.assertNotContains(response, 'release_date__day=')
- self.assertNotContains(response, 'release_date__month=')
- for date in DATES:
- self.assert_contains_year_link(response, date)
-
- for date in DATES:
- url = '%s?release_date__year=%d' % (
- reverse('admin:admin_views_podcast_changelist'),
- date.year)
- response = self.client.get(url)
- self.assert_contains_month_link(response, date)
- self.assert_non_localized_year(response, 2000)
- self.assert_non_localized_year(response, 2003)
- self.assert_non_localized_year(response, 2005)
- url = '%s?release_date__year=%d&release_date__month=%d' % (
- reverse('admin:admin_views_podcast_changelist'),
- date.year, date.month)
- response = self.client.get(url)
- self.assert_contains_day_link(response, date)
- self.assert_non_localized_year(response, 2000)
- self.assert_non_localized_year(response, 2003)
- self.assert_non_localized_year(response, 2005)
|