123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005400640074008400940104011401240134014401540164017401840194020402140224023402440254026402740284029403040314032403340344035403640374038403940404041404240434044404540464047404840494050405140524053405440554056405740584059406040614062406340644065406640674068406940704071407240734074407540764077407840794080408140824083408440854086408740884089409040914092409340944095409640974098409941004101410241034104410541064107410841094110411141124113411441154116411741184119412041214122412341244125412641274128412941304131413241334134413541364137413841394140414141424143414441454146414741484149415041514152415341544155415641574158415941604161416241634164416541664167416841694170417141724173417441754176417741784179418041814182418341844185418641874188418941904191419241934194419541964197419841994200420142024203420442054206420742084209421042114212421342144215421642174218421942204221422242234224422542264227422842294230423142324233423442354236423742384239424042414242424342444245424642474248424942504251425242534254425542564257425842594260426142624263426442654266426742684269427042714272427342744275427642774278427942804281428242834284428542864287428842894290429142924293429442954296429742984299430043014302430343044305430643074308430943104311431243134314431543164317431843194320432143224323432443254326432743284329433043314332433343344335433643374338433943404341434243434344434543464347434843494350435143524353435443554356435743584359436043614362436343644365436643674368436943704371437243734374437543764377437843794380438143824383438443854386438743884389439043914392439343944395439643974398439944004401440244034404440544064407440844094410441144124413441444154416441744184419442044214422442344244425442644274428442944304431443244334434443544364437443844394440444144424443444444454446444744484449445044514452445344544455445644574458445944604461446244634464446544664467446844694470447144724473447444754476447744784479448044814482448344844485448644874488448944904491449244934494449544964497449844994500450145024503450445054506450745084509451045114512451345144515451645174518451945204521452245234524452545264527452845294530453145324533453445354536453745384539454045414542454345444545454645474548454945504551455245534554455545564557455845594560456145624563456445654566456745684569457045714572457345744575457645774578457945804581458245834584458545864587458845894590459145924593459445954596459745984599460046014602460346044605460646074608460946104611461246134614461546164617461846194620462146224623462446254626462746284629463046314632463346344635463646374638463946404641464246434644464546464647464846494650465146524653465446554656465746584659466046614662466346644665466646674668466946704671467246734674467546764677467846794680468146824683468446854686468746884689469046914692469346944695469646974698469947004701470247034704470547064707470847094710471147124713471447154716471747184719472047214722472347244725472647274728472947304731473247334734473547364737473847394740474147424743474447454746474747484749475047514752475347544755475647574758475947604761476247634764476547664767476847694770477147724773477447754776477747784779478047814782478347844785478647874788478947904791479247934794479547964797479847994800480148024803480448054806480748084809481048114812481348144815481648174818481948204821482248234824482548264827482848294830483148324833483448354836483748384839484048414842484348444845484648474848484948504851485248534854485548564857485848594860486148624863486448654866486748684869487048714872487348744875487648774878487948804881488248834884488548864887488848894890489148924893489448954896489748984899490049014902490349044905490649074908490949104911491249134914491549164917491849194920492149224923492449254926492749284929493049314932493349344935493649374938493949404941494249434944494549464947494849494950495149524953495449554956495749584959496049614962496349644965496649674968496949704971497249734974497549764977497849794980498149824983498449854986498749884989499049914992499349944995499649974998499950005001500250035004500550065007500850095010501150125013501450155016501750185019502050215022502350245025502650275028502950305031503250335034503550365037503850395040504150425043504450455046504750485049505050515052505350545055505650575058505950605061506250635064506550665067506850695070507150725073507450755076507750785079508050815082508350845085508650875088508950905091509250935094509550965097509850995100510151025103510451055106510751085109511051115112511351145115511651175118511951205121512251235124512551265127512851295130513151325133513451355136513751385139514051415142514351445145514651475148514951505151515251535154515551565157515851595160516151625163516451655166516751685169517051715172517351745175517651775178517951805181518251835184518551865187518851895190519151925193519451955196519751985199 |
- # -*- coding: utf-8 -*-
- from __future__ import unicode_literals
- import os
- import re
- import datetime
- import unittest
- from django.conf import global_settings
- from django.core import mail
- from django.core.checks import Error
- from django.core.files import temp as tempfile
- from django.core.exceptions import ImproperlyConfigured
- from django.core.urlresolvers import (NoReverseMatch,
- get_script_prefix, reverse, set_script_prefix)
- # Register auth models with the admin.
- from django.contrib.auth import get_permission_codename
- from django.contrib.admin import ModelAdmin
- from django.contrib.admin.helpers import ACTION_CHECKBOX_NAME
- from django.contrib.admin.models import LogEntry, DELETION
- from django.contrib.admin.options import TO_FIELD_VAR
- from django.contrib.admin.templatetags.admin_static import static
- from django.contrib.admin.templatetags.admin_urls import add_preserved_filters
- from django.contrib.admin.tests import AdminSeleniumWebDriverTestCase
- from django.contrib.admin.utils import quote
- from django.contrib.admin.validation import ModelAdminValidator
- from django.contrib.admin.views.main import IS_POPUP_VAR
- from django.contrib.auth import REDIRECT_FIELD_NAME
- from django.contrib.auth.models import Group, User, Permission
- from django.contrib.contenttypes.models import ContentType
- from django.contrib.staticfiles.storage import staticfiles_storage
- from django.forms.utils import ErrorList
- from django.template.response import TemplateResponse
- from django.test import TestCase, skipUnlessDBFeature
- from django.test.utils import patch_logger
- from django.test import modify_settings, override_settings
- from django.utils import formats
- from django.utils import translation
- from django.utils.cache import get_max_age
- from django.utils.encoding import iri_to_uri, force_bytes, force_text
- from django.utils.html import escape
- from django.utils.http import urlencode
- from django.utils.six.moves.urllib.parse import parse_qsl, urljoin, urlparse
- from django.utils._os import upath
- from django.utils import six
- # local test models
- 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, ComplexSortedPerson, PluggableSearchPerson, Parent, Child, AdminOrderedField,
- AdminOrderedModelMethod, AdminOrderedAdminMethod, AdminOrderedCallable,
- Report, MainPrepopulated, RelatedPrepopulated, UnorderedObject,
- Simple, UndeletableObject, UnchangeableObject, Choice, ShortMessage,
- Telegram, Pizza, Topping, FilteredManager, City, Restaurant, Worker,
- ParentWithDependentChildren, Character, FieldOverridePost, Color2)
- from .admin import site, site2, CityAdmin
- ERROR_MESSAGE = "Please enter the correct username and password \
- for a staff account. Note that both fields may be case-sensitive."
- @override_settings(PASSWORD_HASHERS=('django.contrib.auth.hashers.SHA1PasswordHasher',),
- ROOT_URLCONF="admin_views.urls",
- USE_I18N=True, USE_L10N=False, LANGUAGE_CODE='en')
- class AdminViewBasicTestCase(TestCase):
- fixtures = ['admin-views-users.xml', 'admin-views-colors.xml',
- 'admin-views-fabrics.xml', 'admin-views-books.xml']
- # Store the bit of the URL where the admin is registered as a class
- # variable. That way we can test a second AdminSite just by subclassing
- # this test case and changing urlbit.
- urlbit = 'admin'
- def setUp(self):
- self.client.login(username='super', password='secret')
- def tearDown(self):
- formats.reset_format_cache()
- def assertContentBefore(self, response, text1, text2, failing_msg=None):
- """
- Testing utility asserting that text1 appears before text2 in response
- content.
- """
- self.assertEqual(response.status_code, 200)
- self.assertLess(response.content.index(force_bytes(text1)), response.content.index(force_bytes(text2)),
- failing_msg)
- class AdminViewBasicTest(AdminViewBasicTestCase):
- def test_trailing_slash_required(self):
- """
- If you leave off the trailing slash, app should redirect and add it.
- """
- response = self.client.get('/test_admin/%s/admin_views/article/add' % self.urlbit)
- self.assertRedirects(response,
- '/test_admin/%s/admin_views/article/add/' % self.urlbit,
- status_code=301)
- def test_admin_static_template_tag(self):
- """
- Test that admin_static.static is pointing to the collectstatic version
- (as django.contrib.collectstatic is in installed apps).
- """
- old_url = staticfiles_storage.base_url
- staticfiles_storage.base_url = '/test/'
- try:
- self.assertEqual(static('path'), '/test/path')
- finally:
- staticfiles_storage.base_url = old_url
- def test_basic_add_GET(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.assertIsInstance(response, TemplateResponse)
- self.assertEqual(response.status_code, 200)
- def test_add_with_GET_args(self):
- response = self.client.get('/test_admin/%s/admin_views/section/add/' % self.urlbit, {'name': 'My Section'})
- self.assertEqual(response.status_code, 200)
- self.assertContains(response, 'value="My Section"',
- msg_prefix="Couldn't find an input with the right value in the response")
- def test_basic_edit_GET(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.assertIsInstance(response, TemplateResponse)
- self.assertEqual(response.status_code, 200)
- def test_basic_edit_GET_string_PK(self):
- """
- 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 test_basic_inheritance_GET_string_PK(self):
- """
- Ensure GET on the change_view works on inherited models (returns an
- HTTP 404 error, see #19951) 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/supervillain/abc/' % self.urlbit)
- self.assertEqual(response.status_code, 404)
- def test_basic_add_POST(self):
- """
- A smoke test to ensure POST on add_view works.
- """
- post_data = {
- "name": "Another Section",
- # inline data
- "article_set-TOTAL_FORMS": "3",
- "article_set-INITIAL_FORMS": "0",
- "article_set-MAX_NUM_FORMS": "0",
- }
- response = self.client.post('/test_admin/%s/admin_views/section/add/' % self.urlbit, post_data)
- self.assertEqual(response.status_code, 302) # redirect somewhere
- def test_popup_add_POST(self):
- """
- Ensure http response from a popup is properly escaped.
- """
- post_data = {
- '_popup': '1',
- 'title': 'title with a new\nline',
- 'content': 'some content',
- 'date_0': '2010-09-10',
- 'date_1': '14:55:39',
- }
- response = self.client.post('/test_admin/%s/admin_views/article/add/' % self.urlbit, post_data)
- self.assertEqual(response.status_code, 200)
- self.assertContains(response, 'dismissAddAnotherPopup')
- self.assertContains(response, 'title with a new\\u000Aline')
- # Post data for edit inline
- inline_post_data = {
- "name": "Test section",
- # inline data
- "article_set-TOTAL_FORMS": "6",
- "article_set-INITIAL_FORMS": "3",
- "article_set-MAX_NUM_FORMS": "0",
- "article_set-0-id": "1",
- # there is no title in database, give one here or formset will fail.
- "article_set-0-title": "Norske bostaver æøå skaper problemer",
- "article_set-0-content": "<p>Middle content</p>",
- "article_set-0-date_0": "2008-03-18",
- "article_set-0-date_1": "11:54:58",
- "article_set-0-section": "1",
- "article_set-1-id": "2",
- "article_set-1-title": "Need a title.",
- "article_set-1-content": "<p>Oldest content</p>",
- "article_set-1-date_0": "2000-03-18",
- "article_set-1-date_1": "11:54:58",
- "article_set-2-id": "3",
- "article_set-2-title": "Need a title.",
- "article_set-2-content": "<p>Newest content</p>",
- "article_set-2-date_0": "2009-03-18",
- "article_set-2-date_1": "11:54:58",
- "article_set-3-id": "",
- "article_set-3-title": "",
- "article_set-3-content": "",
- "article_set-3-date_0": "",
- "article_set-3-date_1": "",
- "article_set-4-id": "",
- "article_set-4-title": "",
- "article_set-4-content": "",
- "article_set-4-date_0": "",
- "article_set-4-date_1": "",
- "article_set-5-id": "",
- "article_set-5-title": "",
- "article_set-5-content": "",
- "article_set-5-date_0": "",
- "article_set-5-date_1": "",
- }
- def test_basic_edit_POST(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) # redirect somewhere
- def test_edit_save_as(self):
- """
- Test "save as".
- """
- post_data = self.inline_post_data.copy()
- post_data.update({
- '_saveasnew': 'Save+as+new',
- "article_set-1-section": "1",
- "article_set-2-section": "1",
- "article_set-3-section": "1",
- "article_set-4-section": "1",
- "article_set-5-section": "1",
- })
- response = self.client.post('/test_admin/%s/admin_views/section/1/' % self.urlbit, post_data)
- self.assertEqual(response.status_code, 302) # redirect somewhere
- def test_edit_save_as_delete_inline(self):
- """
- Should be able to "Save as new" while also deleting an inline.
- """
- post_data = self.inline_post_data.copy()
- post_data.update({
- '_saveasnew': 'Save+as+new',
- "article_set-1-section": "1",
- "article_set-2-section": "1",
- "article_set-2-DELETE": "1",
- "article_set-3-section": "1",
- })
- response = self.client.post('/test_admin/%s/admin_views/section/1/' % self.urlbit, post_data)
- self.assertEqual(response.status_code, 302)
- # started with 3 articles, one was deleted.
- self.assertEqual(Section.objects.latest('id').article_set.count(), 2)
- def test_change_list_sorting_callable(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, {'o': 2})
- self.assertContentBefore(response, 'Oldest content', 'Middle content',
- "Results of sorting on callable are out of order.")
- self.assertContentBefore(response, 'Middle content', 'Newest content',
- "Results of sorting on callable are out of order.")
- def test_change_list_sorting_model(self):
- """
- Ensure we can sort on a list_display field that is a Model method
- (column 3 is 'model_year' in ArticleAdmin)
- """
- response = self.client.get('/test_admin/%s/admin_views/article/' % self.urlbit, {'o': '-3'})
- self.assertContentBefore(response, 'Newest content', 'Middle content',
- "Results of sorting on Model method are out of order.")
- self.assertContentBefore(response, 'Middle content', 'Oldest content',
- "Results of sorting on Model method are out of order.")
- def test_change_list_sorting_model_admin(self):
- """
- Ensure we can sort on a list_display field that is a ModelAdmin method
- (column 4 is 'modeladmin_year' in ArticleAdmin)
- """
- response = self.client.get('/test_admin/%s/admin_views/article/' % self.urlbit, {'o': '4'})
- self.assertContentBefore(response, 'Oldest content', 'Middle content',
- "Results of sorting on ModelAdmin method are out of order.")
- self.assertContentBefore(response, 'Middle content', 'Newest content',
- "Results of sorting on ModelAdmin method are out of order.")
- def test_change_list_sorting_model_admin_reverse(self):
- """
- Ensure we can sort on a list_display field that is a ModelAdmin
- method in reverse order (i.e. admin_order_field uses the '-' prefix)
- (column 6 is 'model_year_reverse' in ArticleAdmin)
- """
- response = self.client.get('/test_admin/%s/admin_views/article/' % self.urlbit, {'o': '6'})
- self.assertContentBefore(response, '2009', '2008',
- "Results of sorting on ModelAdmin method are out of order.")
- self.assertContentBefore(response, '2008', '2000',
- "Results of sorting on ModelAdmin method are out of order.")
- # Let's make sure the ordering is right and that we don't get a
- # FieldError when we change to descending order
- response = self.client.get('/test_admin/%s/admin_views/article/' % self.urlbit, {'o': '-6'})
- self.assertContentBefore(response, '2000', '2008',
- "Results of sorting on ModelAdmin method are out of order.")
- self.assertContentBefore(response, '2008', '2009',
- "Results of sorting on ModelAdmin method are out of order.")
- def test_change_list_sorting_multiple(self):
- p1 = Person.objects.create(name="Chris", gender=1, alive=True)
- p2 = Person.objects.create(name="Chris", gender=2, alive=True)
- p3 = Person.objects.create(name="Bob", gender=1, alive=True)
- link1 = reverse('admin:admin_views_person_change', args=(p1.pk,))
- link2 = reverse('admin:admin_views_person_change', args=(p2.pk,))
- link3 = reverse('admin:admin_views_person_change', args=(p3.pk,))
- # Sort by name, gender
- # This hard-codes the URL because it'll fail if it runs against the
- # 'admin2' custom admin (which doesn't have the Person model).
- response = self.client.get('/test_admin/admin/admin_views/person/', {'o': '1.2'})
- self.assertContentBefore(response, link3, link1)
- self.assertContentBefore(response, link1, link2)
- # Sort by gender descending, name
- response = self.client.get('/test_admin/admin/admin_views/person/', {'o': '-2.1'})
- self.assertContentBefore(response, link2, link3)
- self.assertContentBefore(response, link3, link1)
- def test_change_list_sorting_preserve_queryset_ordering(self):
- """
- If no ordering is defined in `ModelAdmin.ordering` or in the query
- string, then the underlying order of the queryset should not be
- changed, even if it is defined in `Modeladmin.get_queryset()`.
- Refs #11868, #7309.
- """
- p1 = Person.objects.create(name="Amy", gender=1, alive=True, age=80)
- p2 = Person.objects.create(name="Bob", gender=1, alive=True, age=70)
- p3 = Person.objects.create(name="Chris", gender=2, alive=False, age=60)
- link1 = reverse('admin:admin_views_person_change', args=(p1.pk,))
- link2 = reverse('admin:admin_views_person_change', args=(p2.pk,))
- link3 = reverse('admin:admin_views_person_change', args=(p3.pk,))
- # This hard-codes the URL because it'll fail if it runs against the
- # 'admin2' custom admin (which doesn't have the Person model).
- response = self.client.get('/test_admin/admin/admin_views/person/', {})
- self.assertContentBefore(response, link3, link2)
- self.assertContentBefore(response, link2, link1)
- def test_change_list_sorting_model_meta(self):
- # Test ordering on Model Meta is respected
- l1 = Language.objects.create(iso='ur', name='Urdu')
- l2 = Language.objects.create(iso='ar', name='Arabic')
- link1 = reverse('admin:admin_views_language_change', args=(quote(l1.pk),))
- link2 = reverse('admin:admin_views_language_change', args=(quote(l2.pk),))
- response = self.client.get('/test_admin/admin/admin_views/language/', {})
- self.assertContentBefore(response, link2, link1)
- # Test we can override with query string
- response = self.client.get('/test_admin/admin/admin_views/language/', {'o': '-1'})
- self.assertContentBefore(response, link1, link2)
- def test_change_list_sorting_override_model_admin(self):
- # Test ordering on Model Admin is respected, and overrides Model Meta
- dt = datetime.datetime.now()
- p1 = Podcast.objects.create(name="A", release_date=dt)
- p2 = Podcast.objects.create(name="B", release_date=dt - datetime.timedelta(10))
- link1 = reverse('admin:admin_views_podcast_change', args=(p1.pk,))
- link2 = reverse('admin:admin_views_podcast_change', args=(p2.pk,))
- response = self.client.get('/test_admin/admin/admin_views/podcast/', {})
- self.assertContentBefore(response, link1, link2)
- def test_multiple_sort_same_field(self):
- # Check that we get the columns we expect if we have two columns
- # that correspond to the same ordering field
- dt = datetime.datetime.now()
- p1 = Podcast.objects.create(name="A", release_date=dt)
- p2 = Podcast.objects.create(name="B", release_date=dt - datetime.timedelta(10))
- link1 = reverse('admin:admin_views_podcast_change', args=(quote(p1.pk),))
- link2 = reverse('admin:admin_views_podcast_change', args=(quote(p2.pk),))
- response = self.client.get('/test_admin/admin/admin_views/podcast/', {})
- self.assertContentBefore(response, link1, link2)
- p1 = ComplexSortedPerson.objects.create(name="Bob", age=10)
- p2 = ComplexSortedPerson.objects.create(name="Amy", age=20)
- link1 = reverse('admin:admin_views_complexsortedperson_change', args=(p1.pk,))
- link2 = reverse('admin:admin_views_complexsortedperson_change', args=(p2.pk,))
- response = self.client.get('/test_admin/admin/admin_views/complexsortedperson/', {})
- # Should have 5 columns (including action checkbox col)
- self.assertContains(response, '<th scope="col"', count=5)
- self.assertContains(response, 'Name')
- self.assertContains(response, 'Colored name')
- # Check order
- self.assertContentBefore(response, 'Name', 'Colored name')
- # Check sorting - should be by name
- self.assertContentBefore(response, link2, link1)
- def test_sort_indicators_admin_order(self):
- """
- Ensures that the admin shows default sort indicators for all
- kinds of 'ordering' fields: field names, method on the model
- admin and model itself, and other callables. See #17252.
- """
- models = [(AdminOrderedField, 'adminorderedfield'),
- (AdminOrderedModelMethod, 'adminorderedmodelmethod'),
- (AdminOrderedAdminMethod, 'adminorderedadminmethod'),
- (AdminOrderedCallable, 'adminorderedcallable')]
- for model, url in models:
- model.objects.create(stuff='The Last Item', order=3)
- model.objects.create(stuff='The First Item', order=1)
- model.objects.create(stuff='The Middle Item', order=2)
- response = self.client.get('/test_admin/admin/admin_views/%s/' % url, {})
- self.assertEqual(response.status_code, 200)
- # Should have 3 columns including action checkbox col.
- self.assertContains(response, '<th scope="col"', count=3, msg_prefix=url)
- # Check if the correct column was selected. 2 is the index of the
- # 'order' column in the model admin's 'list_display' with 0 being
- # the implicit 'action_checkbox' and 1 being the column 'stuff'.
- self.assertEqual(response.context['cl'].get_ordering_field_columns(), {2: 'asc'})
- # Check order of records.
- self.assertContentBefore(response, 'The First Item', 'The Middle Item')
- self.assertContentBefore(response, 'The Middle Item', 'The Last Item')
- def test_limited_filter(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.assertContains(response, '<div id="changelist-filter">',
- msg_prefix="Expected filter not found in changelist view")
- self.assertNotContains(response, '<a href="?color__id__exact=3">Blue</a>',
- msg_prefix="Changelist filter not correctly limited by limit_choices_to")
- def test_relation_spanning_filters(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})
- # ensure filter link exists
- self.assertContains(response, '<a href="?%s">' % query_string)
- # ensure link works
- filtered_response = self.client.get(
- '/test_admin/%s/admin_views/chapterxtra1/?%s' % (
- self.urlbit, query_string))
- self.assertEqual(filtered_response.status_code, 200)
- # ensure changelist contains only valid objects
- for obj in filtered_response.context['cl'].queryset.all():
- self.assertTrue(params['test'](obj, value))
- def test_incorrect_lookup_parameters(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)
- # Spanning relationships through a nonexistent related object (Refs #16716)
- response = self.client.get('/test_admin/%s/admin_views/thing/' % self.urlbit, {'notarealfield__whatever': '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)
- # Regression test for #18530
- response = self.client.get('/test_admin/%s/admin_views/thing/' % self.urlbit, {'pub_date__gte': 'foo'})
- self.assertRedirects(response, '/test_admin/%s/admin_views/thing/?e=1' % self.urlbit)
- def test_isnull_lookups(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.assertContains(response, '4 articles')
- response = self.client.get('/test_admin/%s/admin_views/article/' % self.urlbit, {'section__isnull': 'false'})
- self.assertContains(response, '3 articles')
- response = self.client.get('/test_admin/%s/admin_views/article/' % self.urlbit, {'section__isnull': '0'})
- self.assertContains(response, '3 articles')
- response = self.client.get('/test_admin/%s/admin_views/article/' % self.urlbit, {'section__isnull': 'true'})
- self.assertContains(response, '1 article')
- response = self.client.get('/test_admin/%s/admin_views/article/' % self.urlbit, {'section__isnull': '1'})
- self.assertContains(response, '1 article')
- def test_logout_and_password_change_URLs(self):
- response = self.client.get('/test_admin/%s/admin_views/article/' % self.urlbit)
- self.assertContains(response, '<a href="/test_admin/%s/logout/">' % self.urlbit)
- self.assertContains(response, '<a href="/test_admin/%s/password_change/">' % self.urlbit)
- def test_named_group_field_choices_change_list(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.
- """
- link1 = reverse('admin:admin_views_fabric_change', args=(1,), current_app=self.urlbit)
- link2 = reverse('admin:admin_views_fabric_change', args=(2,), current_app=self.urlbit)
- response = self.client.get('/test_admin/%s/admin_views/fabric/' % self.urlbit)
- fail_msg = "Changelist table isn't showing the right human-readable values set by a model field 'choices' option named group."
- self.assertContains(response, '<a href="%s">Horizontal</a>' % link1, msg_prefix=fail_msg, html=True)
- self.assertContains(response, '<a href="%s">Vertical</a>' % link2, msg_prefix=fail_msg, html=True)
- def test_named_group_field_choices_filter(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)
- fail_msg = "Changelist filter isn't showing options contained inside a model field 'choices' option named group."
- self.assertContains(response, '<div id="changelist-filter">')
- self.assertContains(response,
- '<a href="?surface__exact=x">Horizontal</a>', msg_prefix=fail_msg, html=True)
- self.assertContains(response,
- '<a href="?surface__exact=y">Vertical</a>', msg_prefix=fail_msg, html=True)
- def test_change_list_null_boolean_display(self):
- Post.objects.create(public=None)
- # This hard-codes the URl because it'll fail if it runs
- # against the 'admin2' custom admin (which doesn't have the
- # Post model).
- response = self.client.get("/test_admin/admin/admin_views/post/")
- self.assertContains(response, 'icon-unknown.gif')
- def test_i18n_language_non_english_default(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.
- """
- with self.settings(LANGUAGE_CODE='fr'), translation.override('en-us'):
- response = self.client.get('/test_admin/admin/jsi18n/')
- self.assertNotContains(response, 'Choisir une heure')
- def test_i18n_language_non_english_fallback(self):
- """
- Makes sure that the fallback language is still working properly
- in cases where the selected language cannot be found.
- """
- with self.settings(LANGUAGE_CODE='fr'), translation.override('none'):
- response = self.client.get('/test_admin/admin/jsi18n/')
- self.assertContains(response, 'Choisir une heure')
- def test_L10N_deactivated(self):
- """
- Check if L10N is deactivated, the JavaScript i18n view doesn't
- return localized date/time formats. Refs #14824.
- """
- with self.settings(LANGUAGE_CODE='ru', USE_L10N=False), translation.override('none'):
- 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')
- def test_disallowed_filtering(self):
- with patch_logger('django.security.DisallowedModelAdminLookup', 'error') as calls:
- response = self.client.get("/test_admin/admin/admin_views/album/?owner__email__startswith=fuzzy")
- self.assertEqual(response.status_code, 400)
- self.assertEqual(len(calls), 1)
- # Filters are allowed if explicitly included in list_filter
- response = self.client.get("/test_admin/admin/admin_views/thing/?color__value__startswith=red")
- self.assertEqual(response.status_code, 200)
- response = self.client.get("/test_admin/admin/admin_views/thing/?color__value=red")
- self.assertEqual(response.status_code, 200)
- # Filters should be allowed if they involve a local field without the
- # need to whitelist them in list_filter or date_hierarchy.
- response = self.client.get("/test_admin/admin/admin_views/person/?age__gt=30")
- self.assertEqual(response.status_code, 200)
- 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_disallowed_to_field(self):
- with patch_logger('django.security.DisallowedModelAdminToField', 'error') as calls:
- response = self.client.get("/test_admin/admin/admin_views/section/", {TO_FIELD_VAR: 'missing_field'})
- self.assertEqual(response.status_code, 400)
- self.assertEqual(len(calls), 1)
- # Specifying a field that is not referred by any other model registered
- # to this admin site should raise an exception.
- with patch_logger('django.security.DisallowedModelAdminToField', 'error') as calls:
- response = self.client.get("/test_admin/admin/admin_views/section/", {TO_FIELD_VAR: 'name'})
- self.assertEqual(response.status_code, 400)
- self.assertEqual(len(calls), 1)
- # #23839 - Primary key should always be allowed, even if the referenced model isn't registered.
- response = self.client.get("/test_admin/admin/admin_views/notreferenced/", {TO_FIELD_VAR: 'id'})
- self.assertEqual(response.status_code, 200)
- # #23915 - Specifying a field referenced by another model though a m2m should be allowed.
- response = self.client.get("/test_admin/admin/admin_views/recipe/", {TO_FIELD_VAR: 'rname'})
- self.assertEqual(response.status_code, 200)
- # #23604, #23915 - Specifying a field referenced through a reverse m2m relationship should be allowed.
- response = self.client.get("/test_admin/admin/admin_views/ingredient/", {TO_FIELD_VAR: 'iname'})
- self.assertEqual(response.status_code, 200)
- # #23329 - Specifying a field that is not referred by any other model directly registered
- # to this admin site but registered through inheritance should be allowed.
- response = self.client.get("/test_admin/admin/admin_views/referencedbyparent/", {TO_FIELD_VAR: 'name'})
- self.assertEqual(response.status_code, 200)
- # #23431 - Specifying a field that is only referred to by a inline of a registered
- # model should be allowed.
- response = self.client.get("/test_admin/admin/admin_views/referencedbyinline/", {TO_FIELD_VAR: 'name'})
- self.assertEqual(response.status_code, 200)
- # We also want to prevent the add and change view from leaking a
- # disallowed field value.
- with patch_logger('django.security.DisallowedModelAdminToField', 'error') as calls:
- response = self.client.post("/test_admin/admin/admin_views/section/add/", {TO_FIELD_VAR: 'name'})
- self.assertEqual(response.status_code, 400)
- self.assertEqual(len(calls), 1)
- section = Section.objects.create()
- with patch_logger('django.security.DisallowedModelAdminToField', 'error') as calls:
- response = self.client.post("/test_admin/admin/admin_views/section/%d/" % section.pk, {TO_FIELD_VAR: 'name'})
- self.assertEqual(response.status_code, 400)
- self.assertEqual(len(calls), 1)
- 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.
- """
- # Filters should be allowed if they are defined on a ForeignKey pointing to this model
- response = self.client.get("/test_admin/admin/admin_views/inquisition/?leader__name=Palin&leader__age=27")
- self.assertEqual(response.status_code, 200)
- def test_popup_dismiss_related(self):
- """
- Regression test for ticket 20664 - ensure the pk is properly quoted.
- """
- actor = Actor.objects.create(name="Palin", age=27)
- response = self.client.get("/test_admin/admin/admin_views/actor/?%s" % IS_POPUP_VAR)
- self.assertContains(response, "opener.dismissRelatedLookupPopup(window, '%s')" % actor.pk)
- def test_hide_change_password(self):
- """
- Tests if the "change password" link in the admin is hidden if the User
- does not have a usable password set.
- (against 9bea85795705d015cdadc82c68b99196a8554f5c)
- """
- user = User.objects.get(username='super')
- user.set_unusable_password()
- user.save()
- response = self.client.get('/test_admin/admin/')
- self.assertNotContains(response, reverse('admin:password_change'),
- msg_prefix='The "change password" link should not be displayed if a user does not have a usable password.')
- def test_change_view_with_show_delete_extra_context(self):
- """
- Ensured that the 'show_delete' context variable in the admin's change
- view actually controls the display of the delete button.
- Refs #10057.
- """
- instance = UndeletableObject.objects.create(name='foo')
- response = self.client.get('/test_admin/%s/admin_views/undeletableobject/%d/' %
- (self.urlbit, instance.pk))
- self.assertNotContains(response, 'deletelink')
- def test_allows_attributeerror_to_bubble_up(self):
- """
- Ensure that AttributeErrors are allowed to bubble when raised inside
- a change list view.
- Requires a model to be created so there's something to be displayed
- Refs: #16655, #18593, and #18747
- """
- Simple.objects.create()
- with self.assertRaises(AttributeError):
- self.client.get('/test_admin/%s/admin_views/simple/' % self.urlbit)
- def test_changelist_with_no_change_url(self):
- """
- ModelAdmin.changelist_view shouldn't result in a NoReverseMatch if url
- for change_view is removed from get_urls
- Regression test for #20934
- """
- UnchangeableObject.objects.create()
- response = self.client.get('/test_admin/admin/admin_views/unchangeableobject/')
- self.assertEqual(response.status_code, 200)
- # Check the format of the shown object -- shouldn't contain a change link
- self.assertContains(response, '<th class="field-__str__">UnchangeableObject object</th>', html=True)
- def test_invalid_appindex_url(self):
- """
- #21056 -- URL reversing shouldn't work for nonexistent apps.
- """
- good_url = '/test_admin/admin/admin_views/'
- confirm_good_url = reverse('admin:app_list',
- kwargs={'app_label': 'admin_views'})
- self.assertEqual(good_url, confirm_good_url)
- with self.assertRaises(NoReverseMatch):
- reverse('admin:app_list', kwargs={'app_label': 'this_should_fail'})
- with self.assertRaises(NoReverseMatch):
- reverse('admin:app_list', args=('admin_views2',))
- def test_proxy_model_content_type_is_used_for_log_entries(self):
- """
- Log entries for proxy models should have the proxy model's content
- type.
- Regression test for #21084.
- """
- color2_content_type = ContentType.objects.get_for_model(Color2, for_concrete_model=False)
- # add
- color2_add_url = reverse('admin:admin_views_color2_add')
- self.client.post(color2_add_url, {'value': 'orange'})
- color2_addition_log = LogEntry.objects.all()[0]
- self.assertEqual(color2_content_type, color2_addition_log.content_type)
- # change
- color_id = color2_addition_log.object_id
- color2_change_url = reverse('admin:admin_views_color2_change', args=(color_id,))
- self.client.post(color2_change_url, {'value': 'blue'})
- color2_change_log = LogEntry.objects.all()[0]
- self.assertEqual(color2_content_type, color2_change_log.content_type)
- # delete
- color2_delete_url = reverse('admin:admin_views_color2_delete', args=(color_id,))
- self.client.post(color2_delete_url)
- color2_delete_log = LogEntry.objects.all()[0]
- self.assertEqual(color2_content_type, color2_delete_log.content_type)
- def test_adminsite_display_site_url(self):
- """
- #13749 - Admin should display link to front-end site 'View site'
- """
- url = reverse('admin:index')
- response = self.client.get(url)
- self.assertEqual(response.context['site_url'], '/my-site-url/')
- self.assertContains(response, '<a href="/my-site-url/">View site</a>')
- @override_settings(TEMPLATES=[{
- 'BACKEND': 'django.template.backends.django.DjangoTemplates',
- # Put this app's templates dir in DIRS to take precedence over the admin's
- # templates dir.
- 'DIRS': [os.path.join(os.path.dirname(upath(__file__)), 'templates')],
- 'APP_DIRS': True,
- 'OPTIONS': {
- 'context_processors': [
- 'django.template.context_processors.debug',
- 'django.template.context_processors.i18n',
- 'django.template.context_processors.tz',
- 'django.template.context_processors.media',
- 'django.template.context_processors.static',
- 'django.contrib.auth.context_processors.auth',
- 'django.contrib.messages.context_processors.messages',
- ],
- },
- }])
- class AdminCustomTemplateTests(AdminViewBasicTestCase):
- def test_extended_bodyclass_template_change_form(self):
- """
- Ensure that the admin/change_form.html template uses block.super in the
- bodyclass block.
- """
- response = self.client.get('/test_admin/%s/admin_views/section/add/' % self.urlbit)
- self.assertContains(response, 'bodyclass_consistency_check ')
- def test_extended_bodyclass_template_change_password(self):
- """
- Ensure that the auth/user/change_password.html template uses block
- super in the bodyclass block.
- """
- user = User.objects.get(username='super')
- response = self.client.get('/test_admin/%s/auth/user/%s/password/' % (self.urlbit, user.id))
- self.assertContains(response, 'bodyclass_consistency_check ')
- def test_extended_bodyclass_template_index(self):
- """
- Ensure that the admin/index.html template uses block.super in the
- bodyclass block.
- """
- response = self.client.get('/test_admin/%s/' % self.urlbit)
- self.assertContains(response, 'bodyclass_consistency_check ')
- def test_extended_bodyclass_change_list(self):
- """
- Ensure that the admin/change_list.html' template uses block.super
- in the bodyclass block.
- """
- response = self.client.get('/test_admin/%s/admin_views/article/' % self.urlbit)
- self.assertContains(response, 'bodyclass_consistency_check ')
- def test_extended_bodyclass_template_login(self):
- """
- Ensure that the admin/login.html template uses block.super in the
- bodyclass block.
- """
- self.client.logout()
- response = self.client.get('/test_admin/%s/login/' % self.urlbit)
- self.assertContains(response, 'bodyclass_consistency_check ')
- def test_extended_bodyclass_template_delete_confirmation(self):
- """
- Ensure that the admin/delete_confirmation.html template uses
- block.super in the bodyclass block.
- """
- group = Group.objects.create(name="foogroup")
- response = self.client.get('/test_admin/%s/auth/group/%s/delete/' % (self.urlbit, group.id))
- self.assertContains(response, 'bodyclass_consistency_check ')
- def test_extended_bodyclass_template_delete_selected_confirmation(self):
- """
- Ensure that the admin/delete_selected_confirmation.html template uses
- block.super in bodyclass block.
- """
- group = Group.objects.create(name="foogroup")
- post_data = {
- 'action': 'delete_selected',
- 'selected_across': '0',
- 'index': '0',
- '_selected_action': group.id
- }
- response = self.client.post('/test_admin/%s/auth/group/' % (self.urlbit), post_data)
- self.assertEqual(response.context['site_header'], 'Django administration')
- self.assertContains(response, 'bodyclass_consistency_check ')
- def test_filter_with_custom_template(self):
- """
- Ensure that one can use a custom template to render an admin filter.
- Refs #17515.
- """
- response = self.client.get("/test_admin/admin/admin_views/color2/")
- self.assertTemplateUsed(response, 'custom_filter_template.html')
- @override_settings(PASSWORD_HASHERS=('django.contrib.auth.hashers.SHA1PasswordHasher',),
- ROOT_URLCONF="admin_views.urls")
- class AdminViewFormUrlTest(TestCase):
- fixtures = ["admin-views-users.xml"]
- urlbit = "admin3"
- def setUp(self):
- self.client.login(username='super', password='secret')
- def test_change_form_URL_has_correct_value(self):
- """
- Tests whether change_view has form_url in response.context
- """
- response = self.client.get('/test_admin/%s/admin_views/section/1/' % self.urlbit)
- self.assertIn('form_url', response.context, msg='form_url not present in response.context')
- self.assertEqual(response.context['form_url'], 'pony')
- def test_initial_data_can_be_overridden(self):
- """
- Tests that the behavior for setting initial
- form data can be overridden in the ModelAdmin class.
- Usually, the initial value is set via the GET params.
- """
- response = self.client.get('/test_admin/%s/admin_views/restaurant/add/' % self.urlbit, {'name': 'test_value'})
- # this would be the usual behaviour
- self.assertNotContains(response, 'value="test_value"')
- # this is the overridden behaviour
- self.assertContains(response, 'value="overridden_value"')
- @override_settings(PASSWORD_HASHERS=('django.contrib.auth.hashers.SHA1PasswordHasher',),
- ROOT_URLCONF="admin_views.urls")
- class AdminJavaScriptTest(TestCase):
- fixtures = ['admin-views-users.xml']
- def setUp(self):
- self.client.login(username='super', password='secret')
- def test_js_minified_only_if_debug_is_false(self):
- """
- Ensure that the minified versions of the JS files are only used when
- DEBUG is False.
- Refs #17521.
- """
- with override_settings(DEBUG=False):
- response = self.client.get(
- '/test_admin/%s/admin_views/section/add/' % 'admin')
- self.assertNotContains(response, 'jquery.js')
- self.assertContains(response, 'jquery.min.js')
- self.assertNotContains(response, 'prepopulate.js')
- self.assertContains(response, 'prepopulate.min.js')
- self.assertNotContains(response, 'actions.js')
- self.assertContains(response, 'actions.min.js')
- self.assertNotContains(response, 'collapse.js')
- self.assertContains(response, 'collapse.min.js')
- self.assertNotContains(response, 'inlines.js')
- self.assertContains(response, 'inlines.min.js')
- with override_settings(DEBUG=True):
- response = self.client.get(
- '/test_admin/%s/admin_views/section/add/' % 'admin')
- self.assertContains(response, 'jquery.js')
- self.assertNotContains(response, 'jquery.min.js')
- self.assertContains(response, 'prepopulate.js')
- self.assertNotContains(response, 'prepopulate.min.js')
- self.assertContains(response, 'actions.js')
- self.assertNotContains(response, 'actions.min.js')
- self.assertContains(response, 'collapse.js')
- self.assertNotContains(response, 'collapse.min.js')
- self.assertContains(response, 'inlines.js')
- self.assertNotContains(response, 'inlines.min.js')
- @override_settings(PASSWORD_HASHERS=('django.contrib.auth.hashers.SHA1PasswordHasher',),
- ROOT_URLCONF="admin_views.urls")
- class SaveAsTests(TestCase):
- fixtures = ['admin-views-users.xml', 'admin-views-person.xml']
- def setUp(self):
- self.client.login(username='super', password='secret')
- def test_save_as_duplication(self):
- """Ensure save as actually creates a new person"""
- post_data = {'_saveasnew': '', 'name': 'John M', 'gender': 1, 'age': 42}
- 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'], '/test_admin/admin/admin_views/person/add/')
- @override_settings(ROOT_URLCONF="admin_views.urls")
- class CustomModelAdminTest(AdminViewBasicTestCase):
- urlbit = "admin2"
- def test_custom_admin_site_login_form(self):
- self.client.logout()
- response = self.client.get('/test_admin/admin2/', follow=True)
- self.assertIsInstance(response, TemplateResponse)
- self.assertEqual(response.status_code, 200)
- login = self.client.post('/test_admin/admin2/login/', {
- REDIRECT_FIELD_NAME: '/test_admin/admin2/',
- 'username': 'customform',
- 'password': 'secret',
- }, follow=True)
- self.assertIsInstance(login, TemplateResponse)
- self.assertEqual(login.status_code, 200)
- self.assertContains(login, 'custom form error')
- def test_custom_admin_site_login_template(self):
- self.client.logout()
- response = self.client.get('/test_admin/admin2/', follow=True)
- self.assertIsInstance(response, TemplateResponse)
- self.assertTemplateUsed(response, 'custom_admin/login.html')
- self.assertContains(response, 'Hello from a custom login template')
- def test_custom_admin_site_logout_template(self):
- response = self.client.get('/test_admin/admin2/logout/')
- self.assertIsInstance(response, TemplateResponse)
- self.assertTemplateUsed(response, 'custom_admin/logout.html')
- self.assertContains(response, 'Hello from a custom logout template')
- def test_custom_admin_site_index_view_and_template(self):
- try:
- response = self.client.get('/test_admin/admin2/')
- except TypeError:
- self.fail('AdminSite.index_template should accept a list of template paths')
- self.assertIsInstance(response, TemplateResponse)
- self.assertTemplateUsed(response, 'custom_admin/index.html')
- self.assertContains(response, 'Hello from a custom index template *bar*')
- def test_custom_admin_site_app_index_view_and_template(self):
- response = self.client.get('/test_admin/admin2/admin_views/')
- self.assertIsInstance(response, TemplateResponse)
- self.assertTemplateUsed(response, 'custom_admin/app_index.html')
- self.assertContains(response, 'Hello from a custom app_index template')
- def test_custom_admin_site_password_change_template(self):
- response = self.client.get('/test_admin/admin2/password_change/')
- self.assertIsInstance(response, TemplateResponse)
- self.assertTemplateUsed(response, 'custom_admin/password_change_form.html')
- self.assertContains(response, 'Hello from a custom password change form template')
- def test_custom_admin_site_password_change_with_extra_context(self):
- response = self.client.get('/test_admin/admin2/password_change/')
- self.assertIsInstance(response, TemplateResponse)
- self.assertTemplateUsed(response, 'custom_admin/password_change_form.html')
- self.assertContains(response, 'eggs')
- def test_custom_admin_site_password_change_done_template(self):
- response = self.client.get('/test_admin/admin2/password_change/done/')
- self.assertIsInstance(response, TemplateResponse)
- self.assertTemplateUsed(response, 'custom_admin/password_change_done.html')
- self.assertContains(response, 'Hello from a custom password change done template')
- def test_custom_admin_site_view(self):
- self.client.login(username='super', password='secret')
- response = self.client.get('/test_admin/%s/my_view/' % self.urlbit)
- self.assertEqual(response.content, b"Django is a magical pony!")
- def test_pwd_change_custom_template(self):
- self.client.login(username='super', password='secret')
- su = User.objects.get(username='super')
- try:
- response = self.client.get('/test_admin/admin4/auth/user/%s/password/' % su.pk)
- except TypeError:
- self.fail('ModelAdmin.change_user_password_template should accept a list of template paths')
- self.assertEqual(response.status_code, 200)
- 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)
- @override_settings(PASSWORD_HASHERS=('django.contrib.auth.hashers.SHA1PasswordHasher',),
- ROOT_URLCONF="admin_views.urls")
- class AdminViewPermissionsTest(TestCase):
- """Tests for Admin Views Permissions."""
- fixtures = ['admin-views-users.xml']
- def setUp(self):
- """Test setup."""
- # Setup permissions, for our users who can add, change, and delete.
- # We can't put this into the fixture, because the content type id
- # and the permission id could be different on each run of the test.
- opts = Article._meta
- # User who can add Articles
- add_user = User.objects.get(username='adduser')
- add_user.user_permissions.add(get_perm(Article,
- get_permission_codename('add', opts)))
- # User who can change Articles
- change_user = User.objects.get(username='changeuser')
- change_user.user_permissions.add(get_perm(Article,
- get_permission_codename('change', opts)))
- # User who can delete Articles
- delete_user = User.objects.get(username='deleteuser')
- delete_user.user_permissions.add(get_perm(Article,
- get_permission_codename('delete', opts)))
- delete_user.user_permissions.add(get_perm(Section,
- get_permission_codename('delete', Section._meta)))
- # login POST dicts
- self.super_login = {
- REDIRECT_FIELD_NAME: '/test_admin/admin/',
- 'username': 'super',
- 'password': 'secret',
- }
- self.super_email_login = {
- REDIRECT_FIELD_NAME: '/test_admin/admin/',
- 'username': 'super@example.com',
- 'password': 'secret',
- }
- self.super_email_bad_login = {
- REDIRECT_FIELD_NAME: '/test_admin/admin/',
- 'username': 'super@example.com',
- 'password': 'notsecret',
- }
- self.adduser_login = {
- REDIRECT_FIELD_NAME: '/test_admin/admin/',
- 'username': 'adduser',
- 'password': 'secret',
- }
- self.changeuser_login = {
- REDIRECT_FIELD_NAME: '/test_admin/admin/',
- 'username': 'changeuser',
- 'password': 'secret',
- }
- self.deleteuser_login = {
- REDIRECT_FIELD_NAME: '/test_admin/admin/',
- 'username': 'deleteuser',
- 'password': 'secret',
- }
- self.joepublic_login = {
- REDIRECT_FIELD_NAME: '/test_admin/admin/',
- 'username': 'joepublic',
- 'password': 'secret',
- }
- self.no_username_login = {
- REDIRECT_FIELD_NAME: '/test_admin/admin/',
- 'password': 'secret',
- }
- def test_login(self):
- """
- Make sure only staff members can log in.
- Successful posts to the login page will redirect to the original url.
- Unsuccessful attempts will continue to render the login page with
- a 200 status code.
- """
- login_url = reverse('admin:login') + '?next=/test_admin/admin/'
- # Super User
- response = self.client.get('/test_admin/admin/')
- self.assertEqual(response.status_code, 302)
- login = self.client.post(login_url, self.super_login)
- self.assertRedirects(login, '/test_admin/admin/')
- self.assertFalse(login.context)
- self.client.get('/test_admin/admin/logout/')
- # Test if user enters email address
- response = self.client.get('/test_admin/admin/')
- self.assertEqual(response.status_code, 302)
- login = self.client.post(login_url, self.super_email_login)
- self.assertContains(login, ERROR_MESSAGE)
- # only correct passwords get a username hint
- login = self.client.post(login_url, self.super_email_bad_login)
- self.assertContains(login, ERROR_MESSAGE)
- new_user = User(username='jondoe', password='secret', email='super@example.com')
- new_user.save()
- # check to ensure if there are multiple email addresses a user doesn't get a 500
- login = self.client.post(login_url, self.super_email_login)
- self.assertContains(login, ERROR_MESSAGE)
- # Add User
- response = self.client.get('/test_admin/admin/')
- self.assertEqual(response.status_code, 302)
- login = self.client.post(login_url, self.adduser_login)
- self.assertRedirects(login, '/test_admin/admin/')
- self.assertFalse(login.context)
- self.client.get('/test_admin/admin/logout/')
- # Change User
- response = self.client.get('/test_admin/admin/')
- self.assertEqual(response.status_code, 302)
- login = self.client.post(login_url, self.changeuser_login)
- self.assertRedirects(login, '/test_admin/admin/')
- self.assertFalse(login.context)
- self.client.get('/test_admin/admin/logout/')
- # Delete User
- response = self.client.get('/test_admin/admin/')
- self.assertEqual(response.status_code, 302)
- login = self.client.post(login_url, self.deleteuser_login)
- self.assertRedirects(login, '/test_admin/admin/')
- self.assertFalse(login.context)
- self.client.get('/test_admin/admin/logout/')
- # Regular User should not be able to login.
- response = self.client.get('/test_admin/admin/')
- self.assertEqual(response.status_code, 302)
- login = self.client.post(login_url, self.joepublic_login)
- self.assertEqual(login.status_code, 200)
- self.assertContains(login, ERROR_MESSAGE)
- # Requests without username should not return 500 errors.
- response = self.client.get('/test_admin/admin/')
- self.assertEqual(response.status_code, 302)
- login = self.client.post(login_url, 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 test_login_successfully_redirects_to_original_URL(self):
- response = self.client.get('/test_admin/admin/')
- self.assertEqual(response.status_code, 302)
- query_string = 'the-answer=42'
- redirect_url = '/test_admin/admin/?%s' % query_string
- new_next = {REDIRECT_FIELD_NAME: redirect_url}
- post_data = self.super_login.copy()
- post_data.pop(REDIRECT_FIELD_NAME)
- login = self.client.post(
- '%s?%s' % (reverse('admin:login'), urlencode(new_next)),
- post_data)
- self.assertRedirects(login, redirect_url)
- def test_double_login_is_not_allowed(self):
- """Regression test for #19327"""
- login_url = reverse('admin:login') + '?next=/test_admin/admin/'
- response = self.client.get('/test_admin/admin/')
- self.assertEqual(response.status_code, 302)
- # Establish a valid admin session
- login = self.client.post(login_url, self.super_login)
- self.assertRedirects(login, '/test_admin/admin/')
- self.assertFalse(login.context)
- # Logging in with non-admin user fails
- login = self.client.post(login_url, self.joepublic_login)
- self.assertEqual(login.status_code, 200)
- self.assertContains(login, ERROR_MESSAGE)
- # Establish a valid admin session
- login = self.client.post(login_url, self.super_login)
- self.assertRedirects(login, '/test_admin/admin/')
- self.assertFalse(login.context)
- # Logging in with admin user while already logged in
- login = self.client.post(login_url, self.super_login)
- self.assertRedirects(login, '/test_admin/admin/')
- self.assertFalse(login.context)
- self.client.get('/test_admin/admin/logout/')
- def test_add_view(self):
- """Test add view restricts access and actually adds items."""
- login_url = reverse('admin:login') + '?next=/test_admin/admin/'
- add_dict = {'title': 'Døm ikke',
- 'content': '<p>great article</p>',
- 'date_0': '2008-03-18', 'date_1': '10:54:39',
- 'section': 1}
- # Change User should not have access to add articles
- self.client.get('/test_admin/admin/')
- self.client.post(login_url, self.changeuser_login)
- # make sure the view removes test cookie
- self.assertEqual(self.client.session.test_cookie_worked(), False)
- response = self.client.get('/test_admin/admin/admin_views/article/add/')
- self.assertEqual(response.status_code, 403)
- # Try POST just to make sure
- 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/')
- # Add user may login and POST to add view, then redirect to admin root
- self.client.get('/test_admin/admin/')
- self.client.post(login_url, self.adduser_login)
- addpage = self.client.get('/test_admin/admin/admin_views/article/add/')
- change_list_link = '› <a href="/test_admin/admin/admin_views/article/">Articles</a>'
- self.assertNotContains(addpage, change_list_link,
- msg_prefix='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/')
- # Super can add too, but is redirected to the change list view
- self.client.get('/test_admin/admin/')
- self.client.post(login_url, self.super_login)
- addpage = self.client.get('/test_admin/admin/admin_views/article/add/')
- self.assertContains(addpage, change_list_link,
- msg_prefix='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/')
- # 8509 - if a normal user is already logged in, it is possible
- # to change user into the superuser without error
- self.client.login(username='joepublic', password='secret')
- # Check and make sure that if user expires, data still persists
- self.client.get('/test_admin/admin/')
- self.client.post(login_url, self.super_login)
- # make sure the view removes test cookie
- self.assertEqual(self.client.session.test_cookie_worked(), False)
- def test_change_view(self):
- """Change view should restrict access and allow users to edit items."""
- login_url = reverse('admin:login') + '?next=/test_admin/admin/'
- change_dict = {'title': 'Ikke fordømt',
- 'content': '<p>edited article</p>',
- 'date_0': '2008-03-18', 'date_1': '10:54:39',
- 'section': 1}
- # add user should not be able to view the list of article or change any of them
- self.client.get('/test_admin/admin/')
- self.client.post(login_url, self.adduser_login)
- response = self.client.get('/test_admin/admin/admin_views/article/')
- self.assertEqual(response.status_code, 403)
- response = self.client.get('/test_admin/admin/admin_views/article/1/')
- self.assertEqual(response.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/')
- # change user can view all items and edit them
- self.client.get('/test_admin/admin/')
- self.client.post(login_url, self.changeuser_login)
- response = self.client.get('/test_admin/admin/admin_views/article/')
- self.assertEqual(response.status_code, 200)
- response = self.client.get('/test_admin/admin/admin_views/article/1/')
- self.assertEqual(response.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>')
- # one error in form should produce singular error message, multiple errors plural
- change_dict['title'] = ''
- post = self.client.post('/test_admin/admin/admin_views/article/1/', change_dict)
- self.assertContains(post, 'Please correct the error below.',
- msg_prefix='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.assertContains(post, 'Please correct the errors below.',
- msg_prefix='Plural error message not found in response to post with multiple errors')
- self.client.get('/test_admin/admin/logout/')
- # Test redirection when using row-level change permissions. Refs #11513.
- RowLevelChangePermissionModel.objects.create(id=1, name="odd id")
- RowLevelChangePermissionModel.objects.create(id=2, name="even id")
- for login_dict in [self.super_login, self.changeuser_login, self.adduser_login, self.deleteuser_login]:
- self.client.post(login_url, login_dict)
- response = self.client.get('/test_admin/admin/admin_views/rowlevelchangepermissionmodel/1/')
- self.assertEqual(response.status_code, 403)
- response = self.client.post('/test_admin/admin/admin_views/rowlevelchangepermissionmodel/1/', {'name': 'changed'})
- self.assertEqual(RowLevelChangePermissionModel.objects.get(id=1).name, 'odd id')
- self.assertEqual(response.status_code, 403)
- response = self.client.get('/test_admin/admin/admin_views/rowlevelchangepermissionmodel/2/')
- self.assertEqual(response.status_code, 200)
- response = self.client.post('/test_admin/admin/admin_views/rowlevelchangepermissionmodel/2/', {'name': 'changed'})
- self.assertEqual(RowLevelChangePermissionModel.objects.get(id=2).name, 'changed')
- self.assertRedirects(response, '/test_admin/admin/')
- self.client.get('/test_admin/admin/logout/')
- for login_dict in [self.joepublic_login, self.no_username_login]:
- self.client.post(login_url, login_dict)
- response = self.client.get('/test_admin/admin/admin_views/rowlevelchangepermissionmodel/1/', follow=True)
- self.assertEqual(response.status_code, 200)
- self.assertContains(response, 'login-form')
- response = self.client.post('/test_admin/admin/admin_views/rowlevelchangepermissionmodel/1/', {'name': 'changed'}, follow=True)
- self.assertEqual(RowLevelChangePermissionModel.objects.get(id=1).name, 'odd id')
- self.assertEqual(response.status_code, 200)
- self.assertContains(response, 'login-form')
- response = self.client.get('/test_admin/admin/admin_views/rowlevelchangepermissionmodel/2/', follow=True)
- self.assertEqual(response.status_code, 200)
- self.assertContains(response, 'login-form')
- response = self.client.post('/test_admin/admin/admin_views/rowlevelchangepermissionmodel/2/', {'name': 'changed again'}, follow=True)
- self.assertEqual(RowLevelChangePermissionModel.objects.get(id=2).name, 'changed')
- self.assertEqual(response.status_code, 200)
- self.assertContains(response, 'login-form')
- self.client.get('/test_admin/admin/logout/')
- def test_history_view(self):
- """History view should restrict access."""
- login_url = reverse('admin:login') + '?next=/test_admin/admin/'
- # add user should not be able to view the list of article or change any of them
- self.client.get('/test_admin/admin/')
- self.client.post(login_url, self.adduser_login)
- response = self.client.get('/test_admin/admin/admin_views/article/1/history/')
- self.assertEqual(response.status_code, 403)
- self.client.get('/test_admin/admin/logout/')
- # change user can view all items and edit them
- self.client.get('/test_admin/admin/')
- self.client.post(login_url, self.changeuser_login)
- response = self.client.get('/test_admin/admin/admin_views/article/1/history/')
- self.assertEqual(response.status_code, 200)
- # Test redirection when using row-level change permissions. Refs #11513.
- RowLevelChangePermissionModel.objects.create(id=1, name="odd id")
- RowLevelChangePermissionModel.objects.create(id=2, name="even id")
- for login_dict in [self.super_login, self.changeuser_login, self.adduser_login, self.deleteuser_login]:
- self.client.post(login_url, login_dict)
- response = self.client.get('/test_admin/admin/admin_views/rowlevelchangepermissionmodel/1/history/')
- self.assertEqual(response.status_code, 403)
- response = self.client.get('/test_admin/admin/admin_views/rowlevelchangepermissionmodel/2/history/')
- self.assertEqual(response.status_code, 200)
- self.client.get('/test_admin/admin/logout/')
- for login_dict in [self.joepublic_login, self.no_username_login]:
- self.client.post(login_url, login_dict)
- response = self.client.get('/test_admin/admin/admin_views/rowlevelchangepermissionmodel/1/history/', follow=True)
- self.assertEqual(response.status_code, 200)
- self.assertContains(response, 'login-form')
- response = self.client.get('/test_admin/admin/admin_views/rowlevelchangepermissionmodel/2/history/', follow=True)
- self.assertEqual(response.status_code, 200)
- self.assertContains(response, 'login-form')
- self.client.get('/test_admin/admin/logout/')
- def test_conditionally_show_add_section_link(self):
- """
- The foreign key widget should only show the "add related" button if the
- user has permission to add that related item.
- """
- login_url = reverse('admin:login') + '?next=/test_admin/admin/'
- # Set up and log in user.
- url = '/test_admin/admin/admin_views/article/add/'
- add_link_text = ' class="add-another"'
- self.client.get('/test_admin/admin/')
- self.client.post(login_url, self.adduser_login)
- # The add user can't add sections yet, so they shouldn't see the "add
- # section" link.
- response = self.client.get(url)
- self.assertNotContains(response, add_link_text)
- # Allow the add user to add sections too. Now they can see the "add
- # section" link.
- add_user = User.objects.get(username='adduser')
- perm = get_perm(Section, get_permission_codename('add', Section._meta))
- add_user.user_permissions.add(perm)
- response = self.client.get(url)
- self.assertContains(response, add_link_text)
- def test_custom_model_admin_templates(self):
- login_url = reverse('admin:login') + '?next=/test_admin/admin/'
- self.client.get('/test_admin/admin/')
- self.client.post(login_url, self.super_login)
- # Test custom change list template with custom extra context
- response = self.client.get('/test_admin/admin/admin_views/customarticle/')
- self.assertContains(response, "var hello = 'Hello!';")
- self.assertTemplateUsed(response, 'custom_admin/change_list.html')
- # Test custom add form template
- response = self.client.get('/test_admin/admin/admin_views/customarticle/add/')
- self.assertTemplateUsed(response, 'custom_admin/add_form.html')
- # Add an article so we can test delete, change, and history views
- 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)
- article_pk = CustomArticle.objects.all()[0].pk
- # Test custom delete, change, and object history templates
- # Test custom change form template
- response = self.client.get('/test_admin/admin/admin_views/customarticle/%d/' % article_pk)
- self.assertTemplateUsed(response, 'custom_admin/change_form.html')
- response = self.client.get('/test_admin/admin/admin_views/customarticle/%d/delete/' % article_pk)
- self.assertTemplateUsed(response, 'custom_admin/delete_confirmation.html')
- response = self.client.post('/test_admin/admin/admin_views/customarticle/', data={
- 'index': 0,
- 'action': ['delete_selected'],
- '_selected_action': ['1'],
- })
- self.assertTemplateUsed(response, 'custom_admin/delete_selected_confirmation.html')
- response = self.client.get('/test_admin/admin/admin_views/customarticle/%d/history/' % article_pk)
- self.assertTemplateUsed(response, 'custom_admin/object_history.html')
- self.client.get('/test_admin/admin/logout/')
- def test_delete_view(self):
- """Delete view should restrict access and actually delete items."""
- login_url = reverse('admin:login') + '?next=/test_admin/admin/'
- delete_dict = {'post': 'yes'}
- # add user should not be able to delete articles
- self.client.get('/test_admin/admin/')
- self.client.post(login_url, self.adduser_login)
- response = self.client.get('/test_admin/admin/admin_views/article/1/delete/')
- self.assertEqual(response.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/')
- # Delete user can delete
- self.client.get('/test_admin/admin/')
- self.client.post(login_url, self.deleteuser_login)
- response = self.client.get('/test_admin/admin/admin_views/section/1/delete/')
- self.assertContains(response, "<h2>Summary</h2>")
- self.assertContains(response, "<li>Articles: 3</li>")
- # test response contains link to related Article
- self.assertContains(response, "admin_views/article/1/")
- response = self.client.get('/test_admin/admin/admin_views/article/1/delete/')
- self.assertContains(response, "admin_views/article/1/")
- self.assertContains(response, "<h2>Summary</h2>")
- self.assertContains(response, "<li>Articles: 1</li>")
- 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, '1')
- self.client.get('/test_admin/admin/logout/')
- def test_disabled_permissions_when_logged_in(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/', follow=True)
- self.assertContains(response, 'id="login-form"')
- self.assertNotContains(response, 'Log out')
- response = self.client.get('/test_admin/admin/secure-view/', follow=True)
- self.assertContains(response, 'id="login-form"')
- def test_disabled_staff_permissions_when_logged_in(self):
- self.client.login(username='super', password='secret')
- superuser = User.objects.get(username='super')
- superuser.is_staff = False
- superuser.save()
- response = self.client.get('/test_admin/admin/', follow=True)
- self.assertContains(response, 'id="login-form"')
- self.assertNotContains(response, 'Log out')
- response = self.client.get('/test_admin/admin/secure-view/', follow=True)
- self.assertContains(response, 'id="login-form"')
- def test_app_index_fail_early(self):
- """
- If a user has no module perms, avoid iterating over all the modeladmins
- in the registry.
- """
- login_url = reverse('admin:login') + '?next=/test_admin/admin/'
- opts = Article._meta
- change_user = User.objects.get(username='changeuser')
- permission = get_perm(Article, get_permission_codename('change', opts))
- self.client.post(login_url, self.changeuser_login)
- # the user has no module permissions, because this module doesn't exist
- change_user.user_permissions.remove(permission)
- response = self.client.get('/test_admin/admin/admin_views/')
- self.assertEqual(response.status_code, 403)
- # the user now has module permissions
- change_user.user_permissions.add(permission)
- response = self.client.get('/test_admin/admin/admin_views/')
- self.assertEqual(response.status_code, 200)
- def test_shortcut_view_only_available_to_staff(self):
- """
- Only admin users should be able to use the admin shortcut view.
- """
- model_ctype = ContentType.objects.get_for_model(ModelWithStringPrimaryKey)
- obj = ModelWithStringPrimaryKey.objects.create(string_pk='foo')
- shortcut_url = "/test_admin/admin/r/%s/%s/" % (model_ctype.pk, obj.pk)
- # Not logged in: we should see the login page.
- response = self.client.get(shortcut_url, follow=True)
- self.assertTemplateUsed(response, 'admin/login.html')
- # Logged in? Redirect.
- self.client.login(username='super', password='secret')
- response = self.client.get(shortcut_url, follow=False)
- # Can't use self.assertRedirects() because User.get_absolute_url() is silly.
- self.assertEqual(response.status_code, 302)
- # Domain may depend on contrib.sites tests also run
- six.assertRegex(self, response.url, 'http://(testserver|example.com)/dummy/foo/')
- def test_has_module_permission(self):
- """
- Ensure that has_module_permission() returns True for all users who
- have any permission for that module (add, change, or delete), so that
- the module is displayed on the admin index page.
- """
- login_url = reverse('admin:login') + '?next=/test_admin/admin/'
- self.client.post(login_url, self.super_login)
- response = self.client.get('/test_admin/admin/')
- self.assertContains(response, 'admin_views')
- self.assertContains(response, 'Articles')
- self.client.get('/test_admin/admin/logout/')
- self.client.post(login_url, self.adduser_login)
- response = self.client.get('/test_admin/admin/')
- self.assertContains(response, 'admin_views')
- self.assertContains(response, 'Articles')
- self.client.get('/test_admin/admin/logout/')
- self.client.post(login_url, self.changeuser_login)
- response = self.client.get('/test_admin/admin/')
- self.assertContains(response, 'admin_views')
- self.assertContains(response, 'Articles')
- self.client.get('/test_admin/admin/logout/')
- self.client.post(login_url, self.deleteuser_login)
- response = self.client.get('/test_admin/admin/')
- self.assertContains(response, 'admin_views')
- self.assertContains(response, 'Articles')
- self.client.get('/test_admin/admin/logout/')
- def test_overriding_has_module_permission(self):
- """
- Ensure that overriding has_module_permission() has the desired effect.
- In this case, it always returns False, so the module should not be
- displayed on the admin index page for any users.
- """
- login_url = reverse('admin:login') + '?next=/test_admin/admin7/'
- self.client.post(login_url, self.super_login)
- response = self.client.get('/test_admin/admin7/')
- self.assertNotContains(response, 'admin_views')
- self.assertNotContains(response, 'Articles')
- self.client.get('/test_admin/admin7/logout/')
- self.client.post(login_url, self.adduser_login)
- response = self.client.get('/test_admin/admin7/')
- self.assertNotContains(response, 'admin_views')
- self.assertNotContains(response, 'Articles')
- self.client.get('/test_admin/admin7/logout/')
- self.client.post(login_url, self.changeuser_login)
- response = self.client.get('/test_admin/admin7/')
- self.assertNotContains(response, 'admin_views')
- self.assertNotContains(response, 'Articles')
- self.client.get('/test_admin/admin7/logout/')
- self.client.post(login_url, self.deleteuser_login)
- response = self.client.get('/test_admin/admin7/')
- self.assertNotContains(response, 'admin_views')
- self.assertNotContains(response, 'Articles')
- self.client.get('/test_admin/admin7/logout/')
- @override_settings(PASSWORD_HASHERS=('django.contrib.auth.hashers.SHA1PasswordHasher',),
- ROOT_URLCONF="admin_views.urls")
- class AdminViewsNoUrlTest(TestCase):
- """Regression test for #17333"""
- fixtures = ['admin-views-users.xml']
- def setUp(self):
- opts = Report._meta
- # User who can change Reports
- change_user = User.objects.get(username='changeuser')
- change_user.user_permissions.add(get_perm(Report,
- get_permission_codename('change', opts)))
- # login POST dict
- self.changeuser_login = {
- REDIRECT_FIELD_NAME: '/test_admin/admin/',
- 'username': 'changeuser',
- 'password': 'secret',
- }
- def test_no_standard_modeladmin_urls(self):
- """Admin index views don't break when user's ModelAdmin removes standard urls"""
- self.client.get('/test_admin/admin/')
- r = self.client.post(reverse('admin:login'), self.changeuser_login)
- r = self.client.get('/test_admin/admin/')
- # we shouldn' get an 500 error caused by a NoReverseMatch
- self.assertEqual(r.status_code, 200)
- self.client.get('/test_admin/admin/logout/')
- @skipUnlessDBFeature('can_defer_constraint_checks')
- @override_settings(PASSWORD_HASHERS=('django.contrib.auth.hashers.SHA1PasswordHasher',),
- ROOT_URLCONF="admin_views.urls")
- class AdminViewDeletedObjectsTest(TestCase):
- fixtures = ['admin-views-users.xml', 'deleted-objects.xml']
- def setUp(self):
- self.client.login(username='super', password='secret')
- def test_nesting(self):
- """
- Objects should be nested to display the relationships that
- cause them to be scheduled for deletion.
- """
- pattern = re.compile(br"""<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))
- six.assertRegex(self, response.content, pattern)
- 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,
- get_permission_codename('delete', Plot._meta)))
- 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: <a href="/test_admin/admin/admin_views/funkytag/%s/">hott""" % tag.id
- response = self.client.get('/test_admin/admin/admin_views/plot/%s/delete/' % quote(3))
- self.assertContains(response, should_contain)
- @override_settings(PASSWORD_HASHERS=('django.contrib.auth.hashers.SHA1PasswordHasher',),
- ROOT_URLCONF="admin_views.urls")
- class TestGenericRelations(TestCase):
- fixtures = ['admin-views-users.xml', 'deleted-objects.xml']
- def setUp(self):
- self.client.login(username='super', password='secret')
- def test_generic_content_object_in_list_display(self):
- plot = Plot.objects.get(pk=3)
- FunkyTag.objects.create(content_object=plot, name='hott')
- response = self.client.get('/test_admin/admin/admin_views/funkytag/')
- self.assertContains(response, "%s</td>" % plot)
- @override_settings(PASSWORD_HASHERS=('django.contrib.auth.hashers.SHA1PasswordHasher',),
- ROOT_URLCONF="admin_views.urls")
- 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='Changed something')
- def test_get_history_view(self):
- """
- Retrieving the history for an object using urlencoded form of primary
- key should work.
- Refs #12349, #18550.
- """
- response = self.client.get('/test_admin/admin/admin_views/modelwithstringprimarykey/%s/history/' % quote(self.pk))
- self.assertContains(response, escape(self.pk))
- self.assertContains(response, 'Changed something')
- 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):
- "Link to the changeform of the object in changelist should use reverse() and be quoted -- #18072"
- prefix = '/test_admin/admin/admin_views/modelwithstringprimarykey/'
- response = self.client.get(prefix)
- # this URL now comes through reverse(), thus url quoting and iri_to_uri encoding
- pk_final_url = escape(iri_to_uri(quote(self.pk)))
- should_contain = """<th class="field-__str__"><a href="%s%s/">%s</a></th>""" % (prefix, pk_final_url, 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/')
- link = reverse('admin:admin_views_modelwithstringprimarykey_change', args=(quote(self.pk),))
- should_contain = """<a href="%s">%s</a>""" % (escape(link), 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/')
- link = reverse('admin:admin_views_modelwithstringprimarykey_change', args=(quote(self.pk),))
- should_contain = """<a href="%s">%s</a>""" % (escape(link), escape(self.pk))
- self.assertContains(response, should_contain)
- should_contain = "Model with string primary key" # capitalized in Recent Actions
- self.assertContains(response, should_contain)
- logentry = LogEntry.objects.get(content_type__name__iexact=should_contain)
- # http://code.djangoproject.com/ticket/10275
- # if the log entry doesn't have a content type it should still be
- # possible to view the Recent Actions part
- logentry.content_type = None
- logentry.save()
- counted_presence_before = response.content.count(force_bytes(should_contain))
- response = self.client.get('/test_admin/admin/')
- counted_presence_after = response.content.count(force_bytes(should_contain))
- self.assertEqual(counted_presence_before - 1,
- counted_presence_after)
- def test_logentry_get_admin_url(self):
- "LogEntry.get_admin_url returns a URL to edit the entry's object or None for non-existent (possibly deleted) models"
- log_entry_name = "Model with string primary key" # capitalized in Recent Actions
- logentry = LogEntry.objects.get(content_type__name__iexact=log_entry_name)
- model = "modelwithstringprimarykey"
- desired_admin_url = "/test_admin/admin/admin_views/%s/%s/" % (model, iri_to_uri(quote(self.pk)))
- self.assertEqual(logentry.get_admin_url(), desired_admin_url)
- logentry.content_type.model = "non-existent"
- self.assertEqual(logentry.get_admin_url(), None)
- 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))
- # this URL now comes through reverse(), thus url quoting and iri_to_uri encoding
- should_contain = """/%s/">%s</a>""" % (escape(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(pk="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(pk="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(pk="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)
- def test_shortcut_view_with_escaping(self):
- "'View on site should' work properly with char fields"
- model = ModelWithStringPrimaryKey(pk='abc_123')
- model.save()
- response = self.client.get('/test_admin/admin/admin_views/modelwithstringprimarykey/%s/' % quote(model.pk))
- should_contain = '/%s/" class="viewsitelink">' % model.pk
- self.assertContains(response, should_contain)
- def test_change_view_history_link(self):
- """Object history button link should work and contain the pk value quoted."""
- url = reverse('admin:%s_modelwithstringprimarykey_change' %
- ModelWithStringPrimaryKey._meta.app_label,
- args=(quote(self.pk),))
- response = self.client.get(url)
- self.assertEqual(response.status_code, 200)
- expected_link = reverse('admin:%s_modelwithstringprimarykey_history' %
- ModelWithStringPrimaryKey._meta.app_label,
- args=(quote(self.pk),))
- self.assertContains(response, '<a href="%s" class="historylink"' % expected_link)
- def test_redirect_on_add_view_continue_button(self):
- """As soon as an object is added using "Save and continue editing"
- button, the user should be redirected to the object's change_view.
- In case primary key is a string containing some special characters
- like slash or underscore, these characters must be escaped (see #22266)
- """
- response = self.client.post(
- '/test_admin/admin/admin_views/modelwithstringprimarykey/add/',
- {
- 'string_pk': '123/history',
- "_continue": "1", # Save and continue editing
- }
- )
- self.assertEqual(response.status_code, 302) # temporary redirect
- self.assertEqual(
- response['location'],
- (
- 'http://testserver/test_admin/admin/admin_views/'
- 'modelwithstringprimarykey/123_2Fhistory/' # PK is quoted
- )
- )
- @override_settings(PASSWORD_HASHERS=('django.contrib.auth.hashers.SHA1PasswordHasher',),
- ROOT_URLCONF="admin_views.urls")
- class SecureViewTests(TestCase):
- """
- Test behavior of a view protected by the staff_member_required decorator.
- """
- fixtures = ['admin-views-users.xml']
- def test_secure_view_shows_login_if_not_logged_in(self):
- """
- Ensure that we see the admin login form.
- """
- secure_url = '/test_admin/admin/secure-view/'
- response = self.client.get(secure_url)
- self.assertRedirects(response, '%s?next=%s' % (reverse('admin:login'), secure_url))
- response = self.client.get(secure_url, follow=True)
- self.assertTemplateUsed(response, 'admin/login.html')
- self.assertEqual(response.context[REDIRECT_FIELD_NAME], secure_url)
- @override_settings(PASSWORD_HASHERS=('django.contrib.auth.hashers.SHA1PasswordHasher',),
- ROOT_URLCONF="admin_views.urls")
- class AdminViewUnicodeTest(TestCase):
- fixtures = ['admin-views-unicode.xml']
- def setUp(self):
- self.client.login(username='super', password='secret')
- def test_unicode_edit(self):
- """
- A test to ensure that POST on edit_view handles non-ASCII characters.
- """
- post_data = {
- "name": "Test lærdommer",
- # inline data
- "chapter_set-TOTAL_FORMS": "6",
- "chapter_set-INITIAL_FORMS": "3",
- "chapter_set-MAX_NUM_FORMS": "0",
- "chapter_set-0-id": "1",
- "chapter_set-0-title": "Norske bostaver æøå skaper problemer",
- "chapter_set-0-content": "<p>Svært frustrerende med UnicodeDecodeError</p>",
- "chapter_set-1-id": "2",
- "chapter_set-1-title": "Kjærlighet.",
- "chapter_set-1-content": "<p>La kjærligheten til de lidende seire.</p>",
- "chapter_set-2-id": "3",
- "chapter_set-2-title": "Need a title.",
- "chapter_set-2-content": "<p>Newest content</p>",
- "chapter_set-3-id": "",
- "chapter_set-3-title": "",
- "chapter_set-3-content": "",
- "chapter_set-4-id": "",
- "chapter_set-4-title": "",
- "chapter_set-4-content": "",
- "chapter_set-5-id": "",
- "chapter_set-5-title": "",
- "chapter_set-5-content": "",
- }
- response = self.client.post('/test_admin/admin/admin_views/book/1/', post_data)
- self.assertEqual(response.status_code, 302) # redirect somewhere
- def test_unicode_delete(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/')
- @override_settings(PASSWORD_HASHERS=('django.contrib.auth.hashers.SHA1PasswordHasher',),
- ROOT_URLCONF="admin_views.urls")
- class AdminViewListEditable(TestCase):
- fixtures = ['admin-views-users.xml', 'admin-views-person.xml']
- def setUp(self):
- self.client.login(username='super', password='secret')
- 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/')
- # 2 inputs per object(the field and the hidden id field) = 6
- # 4 management hidden fields = 4
- # 4 action inputs (3 regular checkboxes, 1 checkbox to select all)
- # main form submit button = 1
- # search field and search submit button = 2
- # CSRF field = 1
- # field to track 'select all' across paginated views = 1
- # 6 + 4 + 4 + 1 + 2 + 1 + 1 = 19 inputs
- self.assertContains(response, "<input", count=19)
- # 1 select per object = 3 selects
- self.assertContains(response, "<select", count=4)
- def test_post_messages(self):
- # Ticket 12707: Saving inline editable should not show admin
- # action warnings
- 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)
- # test a filtered page
- 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)
- # test a searched page
- 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.
- '''
- fd1 = FoodDelivery.objects.create(reference='123', driver='bill', restaurant='thai')
- fd2 = FoodDelivery.objects.create(reference='456', driver='bill', restaurant='india')
- fd3 = 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": str(fd1.id),
- "form-0-reference": "123",
- "form-0-driver": "bill",
- "form-0-restaurant": "thai",
- # Same data as above: Forbidden because of unique_together!
- "form-1-id": str(fd2.id),
- "form-1-reference": "456",
- "form-1-driver": "bill",
- "form-1-restaurant": "thai",
- "form-2-id": str(fd3.id),
- "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 nonfield"><li>Food delivery with this Driver and Restaurant already exists.</li></ul></td></tr>', 1, html=True)
- data = {
- "form-TOTAL_FORMS": "3",
- "form-INITIAL_FORMS": "3",
- "form-MAX_NUM_FORMS": "0",
- "form-0-id": str(fd1.id),
- "form-0-reference": "123",
- "form-0-driver": "bill",
- "form-0-restaurant": "thai",
- # Same data as above: Forbidden because of unique_together!
- "form-1-id": str(fd2.id),
- "form-1-reference": "456",
- "form-1-driver": "bill",
- "form-1-restaurant": "thai",
- # Same data also.
- "form-2-id": str(fd3.id),
- "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 nonfield"><li>Food delivery with this Driver and Restaurant already exists.</li></ul></td></tr>', 2, html=True)
- def test_non_form_errors(self):
- # test if non-form errors are handled; ticket #12716
- 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",
- # Ensure that the form processing understands this as a list_editable "Save"
- # and not an action "Go".
- "_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):
- # test if non-form errors are correctly handled; ticket #12878
- 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.assertIsInstance(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)
- # NB: The order values must be changed so that the items are reordered.
- 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",
- # Ensure that the form processing understands this as a list_editable "Save"
- # and not an action "Go".
- "_save": "Save",
- }
- response = self.client.post('/test_admin/admin/admin_views/category/', data)
- # Successful post will redirect
- self.assertEqual(response.status_code, 302)
- # Check that the order values have been applied to the right objects
- 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_pagination(self):
- """
- Ensure that pagination works for list_editable items.
- Refs #16819.
- """
- UnorderedObject.objects.create(id=1, name='Unordered object #1')
- UnorderedObject.objects.create(id=2, name='Unordered object #2')
- UnorderedObject.objects.create(id=3, name='Unordered object #3')
- response = self.client.get('/test_admin/admin/admin_views/unorderedobject/')
- self.assertContains(response, 'Unordered object #3')
- self.assertContains(response, 'Unordered object #2')
- self.assertNotContains(response, 'Unordered object #1')
- response = self.client.get('/test_admin/admin/admin_views/unorderedobject/?p=1')
- self.assertNotContains(response, 'Unordered object #3')
- self.assertNotContains(response, 'Unordered object #2')
- self.assertContains(response, 'Unordered object #1')
- def test_list_editable_action_submit(self):
- # List editable changes should not be executed if the action "Go" button is
- # used to submit the form.
- 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": ['3'],
- "action": ['', '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):
- # List editable changes should be executed if the "Save" button is
- # used to submit the form - any action choices should be ignored.
- 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": ['1'],
- "action": ['', '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.
- """
- story1 = Story.objects.create(title='The adventures of Guido', content='Once upon a time in Djangoland...')
- story2 = 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) # Only one hidden field, in a separate place than the table.
- self.assertContains(response, 'id="id_form-1-id"', 1)
- self.assertContains(response, '<div class="hiddenfields">\n<input type="hidden" name="form-0-id" value="%d" id="id_form-0-id" /><input type="hidden" name="form-1-id" value="%d" id="id_form-1-id" />\n</div>' % (story2.id, story1.id), html=True)
- self.assertContains(response, '<td class="field-id">%d</td>' % story1.id, 1)
- self.assertContains(response, '<td class="field-id">%d</td>' % story2.id, 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.
- """
- story1 = OtherStory.objects.create(title='The adventures of Guido', content='Once upon a time in Djangoland...')
- story2 = OtherStory.objects.create(title='Crouching Tiger, Hidden Python', content='The Python was sneaking into...')
- link1 = reverse('admin:admin_views_otherstory_change', args=(story1.pk,))
- link2 = reverse('admin:admin_views_otherstory_change', args=(story2.pk,))
- response = self.client.get('/test_admin/admin/admin_views/otherstory/')
- self.assertContains(response, 'id="id_form-0-id"', 1) # Only one hidden field, in a separate place than the table.
- self.assertContains(response, 'id="id_form-1-id"', 1)
- self.assertContains(response, '<div class="hiddenfields">\n<input type="hidden" name="form-0-id" value="%d" id="id_form-0-id" /><input type="hidden" name="form-1-id" value="%d" id="id_form-1-id" />\n</div>' % (story2.id, story1.id), html=True)
- self.assertContains(response, '<th class="field-id"><a href="%s">%d</a></th>' % (link1, story1.id), 1)
- self.assertContains(response, '<th class="field-id"><a href="%s">%d</a></th>' % (link2, story2.id), 1)
- @override_settings(PASSWORD_HASHERS=('django.contrib.auth.hashers.SHA1PasswordHasher',),
- ROOT_URLCONF="admin_views.urls")
- class AdminSearchTest(TestCase):
- fixtures = ['admin-views-users', 'multiple-child-classes',
- 'admin-views-person']
- def setUp(self):
- self.client.login(username='super', password='secret')
- 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')
- # confirm the search returned 1 object
- 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.
- """
- response = self.client.get('/test_admin/admin/auth/user/?q=joe&%s=id' % TO_FIELD_VAR)
- self.assertContains(response, "\n1 user\n")
- self.assertContains(response, '<input type="hidden" name="%s" value="id"/>' % TO_FIELD_VAR, html=True)
- def test_exact_matches(self):
- response = self.client.get('/test_admin/admin/admin_views/recommendation/?q=bar')
- # confirm the search returned one object
- self.assertContains(response, "\n1 recommendation\n")
- response = self.client.get('/test_admin/admin/admin_views/recommendation/?q=ba')
- # confirm the search returned zero objects
- self.assertContains(response, "\n0 recommendations\n")
- def test_beginning_matches(self):
- response = self.client.get('/test_admin/admin/admin_views/person/?q=Gui')
- # confirm the search returned one object
- self.assertContains(response, "\n1 person\n")
- self.assertContains(response, "Guido")
- response = self.client.get('/test_admin/admin/admin_views/person/?q=uido')
- # confirm the search returned zero objects
- self.assertContains(response, "\n0 persons\n")
- self.assertNotContains(response, "Guido")
- def test_pluggable_search(self):
- PluggableSearchPerson.objects.create(name="Bob", age=10)
- PluggableSearchPerson.objects.create(name="Amy", age=20)
- response = self.client.get('/test_admin/admin/admin_views/pluggablesearchperson/?q=Bob')
- # confirm the search returned one object
- self.assertContains(response, "\n1 pluggable search person\n")
- self.assertContains(response, "Bob")
- response = self.client.get('/test_admin/admin/admin_views/pluggablesearchperson/?q=20')
- # confirm the search returned one object
- self.assertContains(response, "\n1 pluggable search person\n")
- self.assertContains(response, "Amy")
- def test_reset_link(self):
- """
- Test presence of reset link in search bar ("1 result (_x total_)").
- """
- # 1 query for session + 1 for fetching user
- # + 1 for filtered result + 1 for filtered count
- # + 1 for total count
- with self.assertNumQueries(5):
- response = self.client.get('/test_admin/admin/admin_views/person/?q=Gui')
- self.assertContains(response,
- """<span class="small quiet">1 result (<a href="?">3 total</a>)</span>""",
- html=True)
- def test_no_total_count(self):
- """
- #8408 -- "Show all" should be displayed instead of the total count if
- ModelAdmin.show_full_result_count is False.
- """
- # 1 query for session + 1 for fetching user
- # + 1 for filtered result + 1 for filtered count
- with self.assertNumQueries(4):
- response = self.client.get('/test_admin/admin/admin_views/recommendation/?q=bar')
- self.assertContains(response,
- """<span class="small quiet">1 result (<a href="?">Show all</a>)</span>""",
- html=True)
- @override_settings(PASSWORD_HASHERS=('django.contrib.auth.hashers.SHA1PasswordHasher',),
- ROOT_URLCONF="admin_views.urls")
- class AdminInheritedInlinesTest(TestCase):
- fixtures = ['admin-views-users.xml']
- def setUp(self):
- self.client.login(username='super', password='secret')
- def test_inline(self):
- "Ensure that inline models which inherit from a common parent are correctly handled by admin."
- foo_user = "foo username"
- bar_user = "bar username"
- name_re = re.compile(b'name="(.*?)"')
- # test the add case
- response = self.client.get('/test_admin/admin/admin_views/persona/add/')
- names = name_re.findall(response.content)
- # make sure we have no duplicate HTML names
- self.assertEqual(len(names), len(set(names)))
- # test the add case
- post_data = {
- "name": "Test Name",
- # inline data
- "accounts-TOTAL_FORMS": "1",
- "accounts-INITIAL_FORMS": "0",
- "accounts-MAX_NUM_FORMS": "0",
- "accounts-0-username": foo_user,
- "accounts-2-TOTAL_FORMS": "1",
- "accounts-2-INITIAL_FORMS": "0",
- "accounts-2-MAX_NUM_FORMS": "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) # redirect somewhere
- 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)
- persona_id = Persona.objects.all()[0].id
- foo_id = FooAccount.objects.all()[0].id
- bar_id = BarAccount.objects.all()[0].id
- # test the edit case
- response = self.client.get('/test_admin/admin/admin_views/persona/%d/' % persona_id)
- names = name_re.findall(response.content)
- # make sure we have no duplicate HTML names
- self.assertEqual(len(names), len(set(names)))
- post_data = {
- "name": "Test Name",
- "accounts-TOTAL_FORMS": "2",
- "accounts-INITIAL_FORMS": "1",
- "accounts-MAX_NUM_FORMS": "0",
- "accounts-0-username": "%s-1" % foo_user,
- "accounts-0-account_ptr": str(foo_id),
- "accounts-0-persona": str(persona_id),
- "accounts-2-TOTAL_FORMS": "2",
- "accounts-2-INITIAL_FORMS": "1",
- "accounts-2-MAX_NUM_FORMS": "0",
- "accounts-2-0-username": "%s-1" % bar_user,
- "accounts-2-0-account_ptr": str(bar_id),
- "accounts-2-0-persona": str(persona_id),
- }
- response = self.client.post('/test_admin/admin/admin_views/persona/%d/' % persona_id, 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)
- @override_settings(PASSWORD_HASHERS=('django.contrib.auth.hashers.SHA1PasswordHasher',),
- ROOT_URLCONF="admin_views.urls")
- class AdminActionsTest(TestCase):
- fixtures = ['admin-views-users.xml', 'admin-views-actions.xml']
- def setUp(self):
- self.client.login(username='super', password='secret')
- 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,
- }
- 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.assertIsInstance(confirmation, TemplateResponse)
- self.assertContains(confirmation, "Are you sure you want to delete the selected subscribers?")
- self.assertContains(confirmation, "<h2>Summary</h2>")
- self.assertContains(confirmation, "<li>Subscribers: 3</li>")
- self.assertContains(confirmation, "<li>External subscribers: 1</li>")
- self.assertContains(confirmation, ACTION_CHECKBOX_NAME, count=2)
- self.client.post('/test_admin/admin/admin_views/subscriber/', delete_confirmation_data)
- self.assertEqual(Subscriber.objects.count(), 0)
- @override_settings(USE_THOUSAND_SEPARATOR=True, USE_L10N=True)
- 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.
- """
- 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.assertContains(response, 'value="9999"') # Instead of 9,999
- self.assertContains(response, 'value="2"')
- 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, html=True)
- self.assertContains(response, '<li>Answer: <a href="/test_admin/admin/admin_views/answer/%s/">Yes.</a></li>' % a2.pk, html=True)
- def test_model_admin_default_delete_action_no_change_url(self):
- """
- Default delete action shouldn't break if a user's ModelAdmin removes the url for change_view.
- Regression test for #20640
- """
- obj = UnchangeableObject.objects.create()
- action_data = {
- ACTION_CHECKBOX_NAME: obj.pk,
- "action": "delete_selected",
- "index": "0",
- }
- response = self.client.post('/test_admin/admin/admin_views/unchangeableobject/', action_data)
- # No 500 caused by NoReverseMatch
- self.assertEqual(response.status_code, 200)
- # The page shouldn't display a link to the nonexistent change page
- self.assertContains(response, "<li>Unchangeable object: UnchangeableObject object</li>", 1, html=True)
- 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,
- }
- 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/?o=1'
- response = self.client.post(url, action_data)
- self.assertRedirects(response, url)
- def test_custom_function_action_streaming_response(self):
- """Tests a custom action that returns a StreamingHttpResponse."""
- action_data = {
- ACTION_CHECKBOX_NAME: [1],
- 'action': 'download',
- 'index': 0,
- }
- response = self.client.post('/test_admin/admin/admin_views/externalsubscriber/', action_data)
- content = b''.join(response.streaming_content)
- self.assertEqual(content, b'This is the content of the file')
- self.assertEqual(response.status_code, 200)
- def test_custom_function_action_no_perm_response(self):
- """Tests a custom action that returns an HttpResponse with 403 code."""
- action_data = {
- ACTION_CHECKBOX_NAME: [1],
- 'action': 'no_perm',
- 'index': 0,
- }
- response = self.client.post('/test_admin/admin/admin_views/externalsubscriber/', action_data)
- self.assertEqual(response.status_code, 403)
- self.assertEqual(response.content, b'No permission to perform this action')
- def test_actions_ordering(self):
- """
- Ensure that actions are ordered as expected.
- Refs #15964.
- """
- response = self.client.get('/test_admin/admin/admin_views/externalsubscriber/')
- self.assertContains(response, '''<label>Action: <select name="action">
- <option value="" selected="selected">---------</option>
- <option value="delete_selected">Delete selected external
- subscribers</option>
- <option value="redirect_to">Redirect to (Awesome action)</option>
- <option value="external_mail">External mail (Another awesome
- action)</option>
- <option value="download">Download subscription</option>
- <option value="no_perm">No permission to run</option>
- </select>''', html=True)
- 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.assertNotContains(response, '<input type="checkbox" class="action-select"',
- msg_prefix="Found an unexpected action toggle checkboxbox in response")
- self.assertNotContains(response, '<input type="checkbox" class="action-select"')
- 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.assertContains(response, 'jquery.min.js',
- msg_prefix="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.assertContains(response, 'action-checkbox-column')
- 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],
- # Two different actions selected on the two forms...
- 'action': ['external_mail', 'delete_selected'],
- # ...but we clicked "go" on the top form.
- 'index': 0
- }
- self.client.post('/test_admin/admin/admin_views/externalsubscriber/', action_data)
- # Send mail, don't delete.
- 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.assertNotEqual(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)
- def test_popup_template_response(self):
- """
- Success on popups shall be rendered from template in order to allow
- easy customization.
- """
- response = self.client.post(
- '/test_admin/admin/admin_views/actor/add/?%s=1' % IS_POPUP_VAR,
- {'name': 'Troy McClure', 'age': '55', IS_POPUP_VAR: '1'})
- self.assertEqual(response.status_code, 200)
- self.assertEqual(response.template_name, 'admin/popup_response.html')
- @override_settings(PASSWORD_HASHERS=('django.contrib.auth.hashers.SHA1PasswordHasher',),
- ROOT_URLCONF="admin_views.urls")
- 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 test_custom_changelist(self):
- """
- Validate that a custom ChangeList class can be used (#9749)
- """
- # Insert some data
- post_data = {"name": "First Gadget"}
- response = self.client.post('/test_admin/%s/admin_views/gadget/add/' % self.urlbit, post_data)
- self.assertEqual(response.status_code, 302) # redirect somewhere
- # Hit the page once to get messages out of the queue message list
- response = self.client.get('/test_admin/%s/admin_views/gadget/' % self.urlbit)
- # Ensure that data is still not visible on the page
- response = self.client.get('/test_admin/%s/admin_views/gadget/' % self.urlbit)
- self.assertEqual(response.status_code, 200)
- self.assertNotContains(response, 'First Gadget')
- @override_settings(PASSWORD_HASHERS=('django.contrib.auth.hashers.SHA1PasswordHasher',),
- ROOT_URLCONF="admin_views.urls")
- class TestInlineNotEditable(TestCase):
- fixtures = ['admin-views-users.xml']
- def setUp(self):
- result = self.client.login(username='super', password='secret')
- self.assertEqual(result, True)
- def test_GET_parent_add(self):
- """
- InlineModelAdmin broken?
- """
- response = self.client.get('/test_admin/admin/admin_views/parent/add/')
- self.assertEqual(response.status_code, 200)
- @override_settings(PASSWORD_HASHERS=('django.contrib.auth.hashers.SHA1PasswordHasher',),
- ROOT_URLCONF="admin_views.urls")
- 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)]
- self.super_login = {
- REDIRECT_FIELD_NAME: '/test_admin/admin/',
- 'username': 'super',
- 'password': 'secret',
- }
- 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_changelist_view_count_queries(self):
- # create 2 Person objects
- Person.objects.create(name='person1', gender=1)
- Person.objects.create(name='person2', gender=2)
- # 4 queries are expected: 1 for the session, 1 for the user,
- # 1 for the count and 1 for the objects on the page
- with self.assertNumQueries(4):
- resp = self.client.get('/test_admin/admin/admin_views/person/')
- self.assertEqual(resp.context['selection_note'], '0 of 2 selected')
- self.assertEqual(resp.context['selection_note_all'], 'All 2 selected')
- # here one more count(*) query will run, because filters were applied
- with self.assertNumQueries(5):
- extra = {'q': 'not_in_name'}
- resp = self.client.get('/test_admin/admin/admin_views/person/', extra)
- self.assertEqual(resp.context['selection_note'], '0 of 0 selected')
- self.assertEqual(resp.context['selection_note_all'], 'All 0 selected')
- with self.assertNumQueries(5):
- extra = {'q': 'person'}
- resp = self.client.get('/test_admin/admin/admin_views/person/', extra)
- self.assertEqual(resp.context['selection_note'], '0 of 2 selected')
- self.assertEqual(resp.context['selection_note_all'], 'All 2 selected')
- with self.assertNumQueries(5):
- extra = {'gender__exact': '1'}
- resp = self.client.get('/test_admin/admin/admin_views/person/', extra)
- self.assertEqual(resp.context['selection_note'], '0 of 1 selected')
- self.assertEqual(resp.context['selection_note_all'], '1 selected')
- 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_defer_qs(self):
- # Test for #14529. defer() is used in ModelAdmin.get_queryset()
- # model has __unicode__ method
- self.assertEqual(CoverLetter.objects.count(), 0)
- # Emulate model instance creation via the admin
- post_data = {
- "author": "Candidate, Best",
- "_save": "Save",
- }
- response = self.client.post('/test_admin/admin/admin_views/coverletter/add/',
- post_data, follow=True)
- self.assertEqual(response.status_code, 200)
- self.assertEqual(CoverLetter.objects.count(), 1)
- # Message should contain non-ugly model verbose name
- self.assertContains(
- response,
- '<li class="success">The cover letter "Candidate, Best" was added successfully.</li>',
- html=True
- )
- # model has no __unicode__ method
- self.assertEqual(ShortMessage.objects.count(), 0)
- # Emulate model instance creation via the admin
- post_data = {
- "content": "What's this SMS thing?",
- "_save": "Save",
- }
- response = self.client.post('/test_admin/admin/admin_views/shortmessage/add/',
- post_data, follow=True)
- self.assertEqual(response.status_code, 200)
- self.assertEqual(ShortMessage.objects.count(), 1)
- # Message should contain non-ugly model verbose name
- self.assertContains(
- response,
- '<li class="success">The short message "ShortMessage object" was added successfully.</li>',
- html=True
- )
- def test_add_model_modeladmin_only_qs(self):
- # Test for #14529. only() is used in ModelAdmin.get_queryset()
- # model has __unicode__ method
- self.assertEqual(Telegram.objects.count(), 0)
- # Emulate model instance creation via the admin
- post_data = {
- "title": "Urgent telegram",
- "_save": "Save",
- }
- response = self.client.post('/test_admin/admin/admin_views/telegram/add/',
- post_data, follow=True)
- self.assertEqual(response.status_code, 200)
- self.assertEqual(Telegram.objects.count(), 1)
- # Message should contain non-ugly model verbose name
- self.assertContains(
- response,
- '<li class="success">The telegram "Urgent telegram" was added successfully.</li>',
- html=True
- )
- # model has no __unicode__ method
- self.assertEqual(Paper.objects.count(), 0)
- # Emulate model instance creation via the admin
- post_data = {
- "title": "My Modified Paper Title",
- "_save": "Save",
- }
- response = self.client.post('/test_admin/admin/admin_views/paper/add/',
- post_data, follow=True)
- self.assertEqual(response.status_code, 200)
- self.assertEqual(Paper.objects.count(), 1)
- # Message should contain non-ugly model verbose name
- self.assertContains(
- response,
- '<li class="success">The paper "Paper object" was added successfully.</li>',
- html=True
- )
- def test_edit_model_modeladmin_defer_qs(self):
- # Test for #14529. defer() is used in ModelAdmin.get_queryset()
- # model has __unicode__ method
- cl = CoverLetter.objects.create(author="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)
- # Emulate model instance edit via the admin
- post_data = {
- "author": "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.assertEqual(CoverLetter.objects.count(), 1)
- # Message should contain non-ugly model verbose name. Instance
- # representation is set by model's __unicode__()
- self.assertContains(
- response,
- '<li class="success">The cover letter "John Doe II" was changed successfully.</li>',
- html=True
- )
- # model has no __unicode__ method
- sm = ShortMessage.objects.create(content="This is expensive")
- self.assertEqual(ShortMessage.objects.count(), 1)
- response = self.client.get('/test_admin/admin/admin_views/shortmessage/%s/' % sm.pk)
- self.assertEqual(response.status_code, 200)
- # Emulate model instance edit via the admin
- post_data = {
- "content": "Too expensive",
- "_save": "Save",
- }
- response = self.client.post('/test_admin/admin/admin_views/shortmessage/%s/' % sm.pk,
- post_data, follow=True)
- self.assertEqual(response.status_code, 200)
- self.assertEqual(ShortMessage.objects.count(), 1)
- # Message should contain non-ugly model verbose name. The ugly(!)
- # instance representation is set by six.text_type()
- self.assertContains(
- response,
- '<li class="success">The short message "ShortMessage_Deferred_timestamp object" was changed successfully.</li>',
- html=True
- )
- def test_edit_model_modeladmin_only_qs(self):
- # Test for #14529. only() is used in ModelAdmin.get_queryset()
- # model has __unicode__ method
- t = Telegram.objects.create(title="Frist Telegram")
- self.assertEqual(Telegram.objects.count(), 1)
- response = self.client.get('/test_admin/admin/admin_views/telegram/%s/' % t.pk)
- self.assertEqual(response.status_code, 200)
- # Emulate model instance edit via the admin
- post_data = {
- "title": "Telegram without typo",
- "_save": "Save",
- }
- response = self.client.post('/test_admin/admin/admin_views/telegram/%s/' % t.pk,
- post_data, follow=True)
- self.assertEqual(response.status_code, 200)
- self.assertEqual(Telegram.objects.count(), 1)
- # Message should contain non-ugly model verbose name. The instance
- # representation is set by model's __unicode__()
- self.assertContains(
- response,
- '<li class="success">The telegram "Telegram without typo" was changed successfully.</li>',
- html=True
- )
- # model has no __unicode__ method
- p = Paper.objects.create(title="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)
- # Emulate model instance edit via the admin
- post_data = {
- "title": "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.assertEqual(Paper.objects.count(), 1)
- # Message should contain non-ugly model verbose name. The ugly(!)
- # instance representation is set by six.text_type()
- self.assertContains(
- response,
- '<li class="success">The paper "Paper_Deferred_author object" was changed successfully.</li>',
- html=True
- )
- def test_history_view_custom_qs(self):
- """
- Ensure that custom querysets are considered for the admin history view.
- Refs #21013.
- """
- self.client.post(reverse('admin:login'), self.super_login)
- FilteredManager.objects.create(pk=1)
- FilteredManager.objects.create(pk=2)
- response = self.client.get('/test_admin/admin/admin_views/filteredmanager/')
- self.assertContains(response, "PK=1")
- self.assertContains(response, "PK=2")
- self.assertEqual(self.client.get('/test_admin/admin/admin_views/filteredmanager/1/history/').status_code, 200)
- self.assertEqual(self.client.get('/test_admin/admin/admin_views/filteredmanager/2/history/').status_code, 200)
- @override_settings(PASSWORD_HASHERS=('django.contrib.auth.hashers.SHA1PasswordHasher',),
- ROOT_URLCONF="admin_views.urls")
- class AdminInlineFileUploadTest(TestCase):
- fixtures = ['admin-views-users.xml', 'admin-views-actions.xml']
- urlbit = 'admin'
- def setUp(self):
- self.client.login(username='super', password='secret')
- # Set up test Picture and Gallery.
- # These must be set up here instead of in fixtures in order to allow Picture
- # to use a NamedTemporaryFile.
- tdir = tempfile.gettempdir()
- file1 = tempfile.NamedTemporaryFile(suffix=".file1", dir=tdir)
- file1.write(b'a' * (2 ** 21))
- filename = file1.name
- file1.close()
- self.gallery = Gallery(name="Test Gallery")
- self.gallery.save()
- self.picture = Picture(name="Test Picture", image=filename, gallery=self.gallery)
- self.picture.save()
- def test_inline_file_upload_edit_validation_error_post(self):
- """
- Test that inline file uploads correctly display prior data (#10002).
- """
- post_data = {
- "name": "Test Gallery",
- "pictures-TOTAL_FORMS": "2",
- "pictures-INITIAL_FORMS": "1",
- "pictures-MAX_NUM_FORMS": "0",
- "pictures-0-id": six.text_type(self.picture.id),
- "pictures-0-gallery": six.text_type(self.gallery.id),
- "pictures-0-name": "Test Picture",
- "pictures-0-image": "",
- "pictures-1-id": "",
- "pictures-1-gallery": str(self.gallery.id),
- "pictures-1-name": "Test Picture 2",
- "pictures-1-image": "",
- }
- response = self.client.post('/test_admin/%s/admin_views/gallery/%d/' % (self.urlbit, self.gallery.id), post_data)
- self.assertContains(response, b"Currently")
- @override_settings(PASSWORD_HASHERS=('django.contrib.auth.hashers.SHA1PasswordHasher',),
- ROOT_URLCONF="admin_views.urls")
- class AdminInlineTests(TestCase):
- fixtures = ['admin-views-users.xml']
- def setUp(self):
- self.post_data = {
- "name": "Test Name",
- "widget_set-TOTAL_FORMS": "3",
- "widget_set-INITIAL_FORMS": "0",
- "widget_set-MAX_NUM_FORMS": "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": "0",
- "doohickey_set-MAX_NUM_FORMS": "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": "0",
- "grommet_set-MAX_NUM_FORMS": "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": "0",
- "whatsit_set-MAX_NUM_FORMS": "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": "0",
- "fancydoodad_set-MAX_NUM_FORMS": "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 test_simple_inline(self):
- "A simple model can be saved as inlines"
- # First add a new inline
- self.post_data['widget_set-0-name'] = "Widget 1"
- collector_url = '/test_admin/admin/admin_views/collector/%d/' % self.collector.pk
- response = self.client.post(collector_url, self.post_data)
- self.assertEqual(response.status_code, 302)
- self.assertEqual(Widget.objects.count(), 1)
- self.assertEqual(Widget.objects.all()[0].name, "Widget 1")
- widget_id = Widget.objects.all()[0].id
- # Check that the PK link exists on the rendered form
- response = self.client.get(collector_url)
- self.assertContains(response, 'name="widget_set-0-id"')
- # Now resave that inline
- self.post_data['widget_set-INITIAL_FORMS'] = "1"
- self.post_data['widget_set-0-id'] = str(widget_id)
- self.post_data['widget_set-0-name'] = "Widget 1"
- response = self.client.post(collector_url, self.post_data)
- self.assertEqual(response.status_code, 302)
- self.assertEqual(Widget.objects.count(), 1)
- self.assertEqual(Widget.objects.all()[0].name, "Widget 1")
- # Now modify that inline
- self.post_data['widget_set-INITIAL_FORMS'] = "1"
- self.post_data['widget_set-0-id'] = str(widget_id)
- self.post_data['widget_set-0-name'] = "Widget 1 Updated"
- response = self.client.post(collector_url, 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"
- # First add a new inline
- self.post_data['grommet_set-0-name'] = "Grommet 1"
- collector_url = '/test_admin/admin/admin_views/collector/%d/' % self.collector.pk
- response = self.client.post(collector_url, self.post_data)
- self.assertEqual(response.status_code, 302)
- self.assertEqual(Grommet.objects.count(), 1)
- self.assertEqual(Grommet.objects.all()[0].name, "Grommet 1")
- # Check that the PK link exists on the rendered form
- response = self.client.get(collector_url)
- self.assertContains(response, 'name="grommet_set-0-code"')
- # Now resave that inline
- self.post_data['grommet_set-INITIAL_FORMS'] = "1"
- self.post_data['grommet_set-0-code'] = str(Grommet.objects.all()[0].code)
- self.post_data['grommet_set-0-name'] = "Grommet 1"
- response = self.client.post(collector_url, self.post_data)
- self.assertEqual(response.status_code, 302)
- self.assertEqual(Grommet.objects.count(), 1)
- self.assertEqual(Grommet.objects.all()[0].name, "Grommet 1")
- # Now modify that inline
- self.post_data['grommet_set-INITIAL_FORMS'] = "1"
- self.post_data['grommet_set-0-code'] = str(Grommet.objects.all()[0].code)
- self.post_data['grommet_set-0-name'] = "Grommet 1 Updated"
- response = self.client.post(collector_url, 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"
- # First add a new inline
- self.post_data['doohickey_set-0-code'] = "DH1"
- self.post_data['doohickey_set-0-name'] = "Doohickey 1"
- collector_url = '/test_admin/admin/admin_views/collector/%d/' % self.collector.pk
- response = self.client.post(collector_url, self.post_data)
- self.assertEqual(response.status_code, 302)
- self.assertEqual(DooHickey.objects.count(), 1)
- self.assertEqual(DooHickey.objects.all()[0].name, "Doohickey 1")
- # Check that the PK link exists on the rendered form
- response = self.client.get(collector_url)
- self.assertContains(response, 'name="doohickey_set-0-code"')
- # Now resave that inline
- 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(collector_url, self.post_data)
- self.assertEqual(response.status_code, 302)
- self.assertEqual(DooHickey.objects.count(), 1)
- self.assertEqual(DooHickey.objects.all()[0].name, "Doohickey 1")
- # Now modify that inline
- 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(collector_url, 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"
- # First add a new inline
- 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")
- # Check that the PK link exists on the rendered form
- response = self.client.get('/test_admin/admin/admin_views/collector/1/')
- self.assertContains(response, 'name="whatsit_set-0-index"')
- # Now resave that inline
- 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")
- # Now modify that inline
- 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"
- # First add a new inline
- self.post_data['fancydoodad_set-0-name'] = "Fancy Doodad 1"
- collector_url = '/test_admin/admin/admin_views/collector/%d/' % self.collector.pk
- response = self.client.post(collector_url, 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")
- doodad_pk = FancyDoodad.objects.all()[0].pk
- # Check that the PK link exists on the rendered form
- response = self.client.get(collector_url)
- self.assertContains(response, 'name="fancydoodad_set-0-doodad_ptr"')
- # Now resave that inline
- self.post_data['fancydoodad_set-INITIAL_FORMS'] = "1"
- self.post_data['fancydoodad_set-0-doodad_ptr'] = str(doodad_pk)
- self.post_data['fancydoodad_set-0-name'] = "Fancy Doodad 1"
- response = self.client.post(collector_url, 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")
- # Now modify that inline
- self.post_data['fancydoodad_set-INITIAL_FORMS'] = "1"
- self.post_data['fancydoodad_set-0-doodad_ptr'] = str(doodad_pk)
- self.post_data['fancydoodad_set-0-name'] = "Fancy Doodad 1 Updated"
- response = self.client.post(collector_url, 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"""
- # Create some objects with an initial ordering
- 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)
- # NB: The order values must be changed so that the items are reordered.
- 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)
- # Successful post will redirect
- self.assertEqual(response.status_code, 302)
- # Check that the order values have been applied to the right objects
- 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)
- @override_settings(PASSWORD_HASHERS=('django.contrib.auth.hashers.SHA1PasswordHasher',),
- ROOT_URLCONF="admin_views.urls")
- 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 test_admin_index(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 test_app_index(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 test_model_index(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 test_model_add(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 test_model_view(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 test_model_history(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 test_model_delete(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 test_login(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 test_logout(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 test_password_change(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 test_password_change_done(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 test_JS_i18n(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)
- @override_settings(PASSWORD_HASHERS=('django.contrib.auth.hashers.SHA1PasswordHasher',),
- ROOT_URLCONF="admin_views.urls")
- class PrePopulatedTest(TestCase):
- fixtures = ['admin-views-users.xml']
- def setUp(self):
- self.client.login(username='super', password='secret')
- def test_prepopulated_on(self):
- response = self.client.get('/test_admin/admin/admin_views/prepopulatedpost/add/')
- self.assertEqual(response.status_code, 200)
- self.assertContains(response, "id: '#id_slug',")
- self.assertContains(response, "field['dependency_ids'].push('#id_title');")
- self.assertContains(response, "id: '#id_prepopulatedsubpost_set-0-subslug',")
- def test_prepopulated_off(self):
- response = self.client.get('/test_admin/admin/admin_views/prepopulatedpost/1/')
- self.assertEqual(response.status_code, 200)
- self.assertContains(response, "A Long Title")
- self.assertNotContains(response, "id: '#id_slug'")
- self.assertNotContains(response, "field['dependency_ids'].push('#id_title');")
- self.assertNotContains(response, "id: '#id_prepopulatedsubpost_set-0-subslug',")
- @override_settings(USE_THOUSAND_SEPARATOR=True, USE_L10N=True)
- def test_prepopulated_maxlength_localized(self):
- """
- Regression test for #15938: if USE_THOUSAND_SEPARATOR is set, make sure
- that maxLength (in the JavaScript) is rendered without separators.
- """
- response = self.client.get('/test_admin/admin/admin_views/prepopulatedpostlargeslug/add/')
- self.assertContains(response, "maxLength: 1000") # instead of 1,000
- @override_settings(PASSWORD_HASHERS=('django.contrib.auth.hashers.SHA1PasswordHasher',),
- ROOT_URLCONF="admin_views.urls")
- class SeleniumAdminViewsFirefoxTests(AdminSeleniumWebDriverTestCase):
- available_apps = ['admin_views'] + AdminSeleniumWebDriverTestCase.available_apps
- fixtures = ['admin-views-users.xml']
- webdriver_class = 'selenium.webdriver.firefox.webdriver.WebDriver'
- def test_prepopulated_fields(self):
- """
- Ensure that the JavaScript-automated prepopulated fields work with the
- main form and with stacked and tabular inlines.
- Refs #13068, #9264, #9983, #9784.
- """
- self.admin_login(username='super', password='secret', login_url='/test_admin/admin/')
- self.selenium.get('%s%s' % (self.live_server_url,
- '/test_admin/admin/admin_views/mainprepopulated/add/'))
- # Main form ----------------------------------------------------------
- self.selenium.find_element_by_css_selector('#id_pubdate').send_keys('2012-02-18')
- self.get_select_option('#id_status', 'option two').click()
- self.selenium.find_element_by_css_selector('#id_name').send_keys(' this is the mAin nÀMë and it\'s awεšome')
- slug1 = self.selenium.find_element_by_css_selector('#id_slug1').get_attribute('value')
- slug2 = self.selenium.find_element_by_css_selector('#id_slug2').get_attribute('value')
- self.assertEqual(slug1, 'main-name-and-its-awesome-2012-02-18')
- self.assertEqual(slug2, 'option-two-main-name-and-its-awesome')
- # Stacked inlines ----------------------------------------------------
- # Initial inline
- self.selenium.find_element_by_css_selector('#id_relatedprepopulated_set-0-pubdate').send_keys('2011-12-17')
- self.get_select_option('#id_relatedprepopulated_set-0-status', 'option one').click()
- self.selenium.find_element_by_css_selector('#id_relatedprepopulated_set-0-name').send_keys(' here is a sŤāÇkeð inline ! ')
- slug1 = self.selenium.find_element_by_css_selector('#id_relatedprepopulated_set-0-slug1').get_attribute('value')
- slug2 = self.selenium.find_element_by_css_selector('#id_relatedprepopulated_set-0-slug2').get_attribute('value')
- self.assertEqual(slug1, 'here-stacked-inline-2011-12-17')
- self.assertEqual(slug2, 'option-one-here-stacked-inline')
- # Add an inline
- self.selenium.find_elements_by_link_text('Add another Related prepopulated')[0].click()
- self.selenium.find_element_by_css_selector('#id_relatedprepopulated_set-1-pubdate').send_keys('1999-01-25')
- self.get_select_option('#id_relatedprepopulated_set-1-status', 'option two').click()
- self.selenium.find_element_by_css_selector('#id_relatedprepopulated_set-1-name').send_keys(' now you haVe anöther sŤāÇkeð inline with a very ... loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooog text... ')
- slug1 = self.selenium.find_element_by_css_selector('#id_relatedprepopulated_set-1-slug1').get_attribute('value')
- slug2 = self.selenium.find_element_by_css_selector('#id_relatedprepopulated_set-1-slug2').get_attribute('value')
- self.assertEqual(slug1, 'now-you-have-another-stacked-inline-very-loooooooo') # 50 characters maximum for slug1 field
- self.assertEqual(slug2, 'option-two-now-you-have-another-stacked-inline-very-looooooo') # 60 characters maximum for slug2 field
- # Tabular inlines ----------------------------------------------------
- # Initial inline
- self.selenium.find_element_by_css_selector('#id_relatedprepopulated_set-2-0-pubdate').send_keys('1234-12-07')
- self.get_select_option('#id_relatedprepopulated_set-2-0-status', 'option two').click()
- self.selenium.find_element_by_css_selector('#id_relatedprepopulated_set-2-0-name').send_keys('And now, with a tÃbűlaŘ inline !!!')
- slug1 = self.selenium.find_element_by_css_selector('#id_relatedprepopulated_set-2-0-slug1').get_attribute('value')
- slug2 = self.selenium.find_element_by_css_selector('#id_relatedprepopulated_set-2-0-slug2').get_attribute('value')
- self.assertEqual(slug1, 'and-now-tabular-inline-1234-12-07')
- self.assertEqual(slug2, 'option-two-and-now-tabular-inline')
- # Add an inline
- self.selenium.find_elements_by_link_text('Add another Related prepopulated')[1].click()
- self.selenium.find_element_by_css_selector('#id_relatedprepopulated_set-2-1-pubdate').send_keys('1981-08-22')
- self.get_select_option('#id_relatedprepopulated_set-2-1-status', 'option one').click()
- self.selenium.find_element_by_css_selector('#id_relatedprepopulated_set-2-1-name').send_keys('a tÃbűlaŘ inline with ignored ;"&*^\%$#@-/`~ characters')
- slug1 = self.selenium.find_element_by_css_selector('#id_relatedprepopulated_set-2-1-slug1').get_attribute('value')
- slug2 = self.selenium.find_element_by_css_selector('#id_relatedprepopulated_set-2-1-slug2').get_attribute('value')
- self.assertEqual(slug1, 'tabular-inline-ignored-characters-1981-08-22')
- self.assertEqual(slug2, 'option-one-tabular-inline-ignored-characters')
- # Save and check that everything is properly stored in the database
- self.selenium.find_element_by_xpath('//input[@value="Save"]').click()
- self.wait_page_loaded()
- self.assertEqual(MainPrepopulated.objects.all().count(), 1)
- MainPrepopulated.objects.get(
- name=' this is the mAin nÀMë and it\'s awεšome',
- pubdate='2012-02-18',
- status='option two',
- slug1='main-name-and-its-awesome-2012-02-18',
- slug2='option-two-main-name-and-its-awesome',
- )
- self.assertEqual(RelatedPrepopulated.objects.all().count(), 4)
- RelatedPrepopulated.objects.get(
- name=' here is a sŤāÇkeð inline ! ',
- pubdate='2011-12-17',
- status='option one',
- slug1='here-stacked-inline-2011-12-17',
- slug2='option-one-here-stacked-inline',
- )
- RelatedPrepopulated.objects.get(
- name=' now you haVe anöther sŤāÇkeð inline with a very ... loooooooooooooooooo', # 75 characters in name field
- pubdate='1999-01-25',
- status='option two',
- slug1='now-you-have-another-stacked-inline-very-loooooooo',
- slug2='option-two-now-you-have-another-stacked-inline-very-looooooo',
- )
- RelatedPrepopulated.objects.get(
- name='And now, with a tÃbűlaŘ inline !!!',
- pubdate='1234-12-07',
- status='option two',
- slug1='and-now-tabular-inline-1234-12-07',
- slug2='option-two-and-now-tabular-inline',
- )
- RelatedPrepopulated.objects.get(
- name='a tÃbűlaŘ inline with ignored ;"&*^\%$#@-/`~ characters',
- pubdate='1981-08-22',
- status='option one',
- slug1='tabular-inline-ignored-characters-1981-08-22',
- slug2='option-one-tabular-inline-ignored-characters',
- )
- def test_populate_existing_object(self):
- """
- Ensure that the prepopulation works for existing objects too, as long
- as the original field is empty.
- Refs #19082.
- """
- # Slugs are empty to start with.
- item = MainPrepopulated.objects.create(
- name=' this is the mAin nÀMë',
- pubdate='2012-02-18',
- status='option two',
- slug1='',
- slug2='',
- )
- self.admin_login(username='super',
- password='secret',
- login_url='/test_admin/admin/')
- object_url = '%s%s' % (
- self.live_server_url,
- '/test_admin/admin/admin_views/mainprepopulated/{}/'.format(item.id))
- self.selenium.get(object_url)
- self.selenium.find_element_by_css_selector('#id_name').send_keys(' the best')
- # The slugs got prepopulated since they were originally empty
- slug1 = self.selenium.find_element_by_css_selector('#id_slug1').get_attribute('value')
- slug2 = self.selenium.find_element_by_css_selector('#id_slug2').get_attribute('value')
- self.assertEqual(slug1, 'main-name-best-2012-02-18')
- self.assertEqual(slug2, 'option-two-main-name-best')
- # Save the object
- self.selenium.find_element_by_xpath('//input[@value="Save"]').click()
- self.wait_page_loaded()
- self.selenium.get(object_url)
- self.selenium.find_element_by_css_selector('#id_name').send_keys(' hello')
- # The slugs got prepopulated didn't change since they were originally not empty
- slug1 = self.selenium.find_element_by_css_selector('#id_slug1').get_attribute('value')
- slug2 = self.selenium.find_element_by_css_selector('#id_slug2').get_attribute('value')
- self.assertEqual(slug1, 'main-name-best-2012-02-18')
- self.assertEqual(slug2, 'option-two-main-name-best')
- def test_collapsible_fieldset(self):
- """
- Test that the 'collapse' class in fieldsets definition allows to
- show/hide the appropriate field section.
- """
- self.admin_login(username='super', password='secret', login_url='/test_admin/admin/')
- self.selenium.get('%s%s' % (self.live_server_url,
- '/test_admin/admin/admin_views/article/add/'))
- self.assertFalse(self.selenium.find_element_by_id('id_title').is_displayed())
- self.selenium.find_elements_by_link_text('Show')[0].click()
- self.assertTrue(self.selenium.find_element_by_id('id_title').is_displayed())
- self.assertEqual(
- self.selenium.find_element_by_id('fieldsetcollapser0').text,
- "Hide"
- )
- def test_first_field_focus(self):
- """JavaScript-assisted auto-focus on first usable form field."""
- # First form field has a single widget
- self.admin_login(username='super', password='secret', login_url='/test_admin/admin/')
- self.selenium.get('%s%s' % (self.live_server_url,
- '/test_admin/admin/admin_views/picture/add/'))
- self.assertEqual(
- self.selenium.switch_to.active_element,
- self.selenium.find_element_by_id('id_name')
- )
- # First form field has a MultiWidget
- self.selenium.get('%s%s' % (self.live_server_url,
- '/test_admin/admin/admin_views/reservation/add/'))
- self.assertEqual(
- self.selenium.switch_to.active_element,
- self.selenium.find_element_by_id('id_start_date_0')
- )
- def test_cancel_delete_confirmation(self):
- "Cancelling the deletion of an object takes the user back one page."
- pizza = Pizza.objects.create(name="Double Cheese")
- url = reverse('admin:admin_views_pizza_change', args=(pizza.id,))
- full_url = '%s%s' % (self.live_server_url, url)
- self.admin_login(username='super', password='secret', login_url='/test_admin/admin/')
- self.selenium.get(full_url)
- self.selenium.find_element_by_class_name('deletelink').click()
- self.selenium.find_element_by_class_name('cancel-link').click()
- self.assertEqual(self.selenium.current_url, full_url)
- self.assertEqual(Pizza.objects.count(), 1)
- def test_cancel_delete_related_confirmation(self):
- """
- Cancelling the deletion of an object with relations takes the user back
- one page.
- """
- pizza = Pizza.objects.create(name="Double Cheese")
- topping1 = Topping.objects.create(name="Cheddar")
- topping2 = Topping.objects.create(name="Mozzarella")
- pizza.toppings.add(topping1, topping2)
- url = reverse('admin:admin_views_pizza_change', args=(pizza.id,))
- full_url = '%s%s' % (self.live_server_url, url)
- self.admin_login(username='super', password='secret', login_url='/test_admin/admin/')
- self.selenium.get(full_url)
- self.selenium.find_element_by_class_name('deletelink').click()
- self.selenium.find_element_by_class_name('cancel-link').click()
- self.assertEqual(self.selenium.current_url, full_url)
- self.assertEqual(Pizza.objects.count(), 1)
- self.assertEqual(Topping.objects.count(), 2)
- class SeleniumAdminViewsChromeTests(SeleniumAdminViewsFirefoxTests):
- webdriver_class = 'selenium.webdriver.chrome.webdriver.WebDriver'
- class SeleniumAdminViewsIETests(SeleniumAdminViewsFirefoxTests):
- webdriver_class = 'selenium.webdriver.ie.webdriver.WebDriver'
- @override_settings(PASSWORD_HASHERS=('django.contrib.auth.hashers.SHA1PasswordHasher',),
- ROOT_URLCONF="admin_views.urls")
- class ReadonlyTest(TestCase):
- fixtures = ['admin-views-users.xml']
- def setUp(self):
- self.client.login(username='super', password='secret')
- 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"')
- # 3 fields + 2 submit buttons + 5 inline management form fields, + 2
- # hidden fields for inlines + 1 field for the inline + 2 empty form
- self.assertContains(response, "<input", count=15)
- self.assertContains(response, formats.localize(datetime.date.today()))
- self.assertContains(response,
- "<label>Awesomeness level:</label>")
- self.assertContains(response, "Very awesome.")
- self.assertContains(response, "Unknown coolness.")
- self.assertContains(response, "foo")
- # Checks that multiline text in a readonly field gets <br /> tags
- self.assertContains(response, "Multiline<br />test<br />string")
- self.assertContains(response, "<p>Multiline<br />html<br />content</p>", html=True)
- self.assertContains(response, "InlineMultiline<br />test<br />string")
- self.assertContains(response,
- formats.localize(datetime.date.today() - datetime.timedelta(days=7)))
- self.assertContains(response, '<div class="form-row field-coolness">')
- self.assertContains(response, '<div class="form-row field-awesomeness_level">')
- self.assertContains(response, '<div class="form-row field-posted">')
- self.assertContains(response, '<div class="form-row field-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>', html=True)
- self.assertContains(response, '<p class="help">Some help text for the content (with unicode ŠĐĆŽćžšđ)</p>', html=True)
- self.assertContains(response, '<p class="help">Some help text for the date (with unicode ŠĐĆŽćžšđ)</p>', html=True)
- 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" # some date that's not today
- 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)
- def test_user_password_change_limited_queryset(self):
- su = User.objects.filter(is_superuser=True)[0]
- response = self.client.get('/test_admin/admin2/auth/user/%s/password/' % su.pk)
- self.assertEqual(response.status_code, 404)
- def test_change_form_renders_correct_null_choice_value(self):
- """
- Regression test for #17911.
- """
- choice = Choice.objects.create(choice=None)
- response = self.client.get('/test_admin/admin/admin_views/choice/%s/' % choice.pk)
- self.assertContains(response, '<p>No opinion</p>', html=True)
- self.assertNotContains(response, '<p>(None)</p>')
- def test_readonly_backwards_ref(self):
- """
- Regression test for #16433 - backwards references for related objects
- broke if the related field is read-only due to the help_text attribute
- """
- topping = Topping.objects.create(name='Salami')
- pizza = Pizza.objects.create(name='Americano')
- pizza.toppings.add(topping)
- response = self.client.get('/test_admin/admin/admin_views/topping/add/')
- self.assertEqual(response.status_code, 200)
- def test_readonly_field_overrides(self):
- """
- Regression test for #22087 - ModelForm Meta overrides are ignored by
- AdminReadonlyField
- """
- p = FieldOverridePost.objects.create(title="Test Post", content="Test Content")
- response = self.client.get('/test_admin/admin/admin_views/fieldoverridepost/%d/' % p.pk)
- self.assertEqual(response.status_code, 200)
- self.assertContains(response, '<p class="help">Overridden help text for the date</p>')
- self.assertContains(response, '<label for="id_public">Overridden public label:</label>', html=True)
- self.assertNotContains(response, "Some help text for the date (with unicode ŠĐĆŽćžšđ)")
- @override_settings(PASSWORD_HASHERS=('django.contrib.auth.hashers.SHA1PasswordHasher',),
- ROOT_URLCONF="admin_views.urls")
- class LimitChoicesToInAdminTest(TestCase):
- fixtures = ['admin-views-users.xml']
- def setUp(self):
- self.client.login(username='super', password='secret')
- def test_limit_choices_to_as_callable(self):
- """Test for ticket 2445 changes to admin."""
- threepwood = Character.objects.create(
- username='threepwood',
- last_action=datetime.datetime.today() + datetime.timedelta(days=1),
- )
- marley = Character.objects.create(
- username='marley',
- last_action=datetime.datetime.today() - datetime.timedelta(days=1),
- )
- response = self.client.get('/test_admin/admin/admin_views/stumpjoke/add/')
- # The allowed option should appear twice; the limited option should not appear.
- self.assertContains(response, threepwood.username, count=2)
- self.assertNotContains(response, marley.username)
- @override_settings(PASSWORD_HASHERS=('django.contrib.auth.hashers.SHA1PasswordHasher',),
- ROOT_URLCONF="admin_views.urls")
- class RawIdFieldsTest(TestCase):
- fixtures = ['admin-views-users.xml']
- def setUp(self):
- self.client.login(username='super', password='secret')
- def test_limit_choices_to(self):
- """Regression test for 14880"""
- actor = Actor.objects.create(name="Palin", age=27)
- Inquisition.objects.create(expected=True,
- leader=actor,
- country="England")
- Inquisition.objects.create(expected=False,
- leader=actor,
- country="Spain")
- response = self.client.get('/test_admin/admin/admin_views/sketch/add/')
- # Find the link
- m = re.search(br'<a href="([^"]*)"[^>]* id="lookup_id_inquisition"', response.content)
- self.assertTrue(m) # Got a match
- popup_url = m.groups()[0].decode().replace("&", "&")
- # Handle relative links
- popup_url = urljoin(response.request['PATH_INFO'], popup_url)
- # Get the popup and verify the correct objects show up in the resulting
- # page. This step also tests integers, strings and booleans in the
- # lookup query string; in model we define inquisition field to have a
- # limit_choices_to option that includes a filter on a string field
- # (inquisition__actor__name), a filter on an integer field
- # (inquisition__actor__age), and a filter on a boolean field
- # (inquisition__expected).
- response2 = self.client.get(popup_url)
- self.assertContains(response2, "Spain")
- self.assertNotContains(response2, "England")
- def test_limit_choices_to_isnull_false(self):
- """Regression test for 20182"""
- Actor.objects.create(name="Palin", age=27)
- Actor.objects.create(name="Kilbraken", age=50, title="Judge")
- response = self.client.get('/test_admin/admin/admin_views/sketch/add/')
- # Find the link
- m = re.search(br'<a href="([^"]*)"[^>]* id="lookup_id_defendant0"', response.content)
- self.assertTrue(m) # Got a match
- popup_url = m.groups()[0].decode().replace("&", "&")
- # Handle relative links
- popup_url = urljoin(response.request['PATH_INFO'], popup_url)
- # Get the popup and verify the correct objects show up in the resulting
- # page. This step tests field__isnull=0 gets parsed correctly from the
- # lookup query string; in model we define defendant0 field to have a
- # limit_choices_to option that includes "actor__title__isnull=False".
- response2 = self.client.get(popup_url)
- self.assertContains(response2, "Kilbraken")
- self.assertNotContains(response2, "Palin")
- def test_limit_choices_to_isnull_true(self):
- """Regression test for 20182"""
- Actor.objects.create(name="Palin", age=27)
- Actor.objects.create(name="Kilbraken", age=50, title="Judge")
- response = self.client.get('/test_admin/admin/admin_views/sketch/add/')
- # Find the link
- m = re.search(br'<a href="([^"]*)"[^>]* id="lookup_id_defendant1"', response.content)
- self.assertTrue(m) # Got a match
- popup_url = m.groups()[0].decode().replace("&", "&")
- # Handle relative links
- popup_url = urljoin(response.request['PATH_INFO'], popup_url)
- # Get the popup and verify the correct objects show up in the resulting
- # page. This step tests field__isnull=1 gets parsed correctly from the
- # lookup query string; in model we define defendant1 field to have a
- # limit_choices_to option that includes "actor__title__isnull=True".
- response2 = self.client.get(popup_url)
- self.assertNotContains(response2, "Kilbraken")
- self.assertContains(response2, "Palin")
- @override_settings(PASSWORD_HASHERS=('django.contrib.auth.hashers.SHA1PasswordHasher',),
- ROOT_URLCONF="admin_views.urls")
- class UserAdminTest(TestCase):
- """
- Tests user CRUD functionality.
- """
- fixtures = ['admin-views-users.xml']
- def setUp(self):
- self.client.login(username='super', password='secret')
- 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.assertTrue(new_user.has_usable_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.assertTrue(new_user.has_usable_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.assertNotIn('password', adminform.form.errors)
- self.assertEqual(adminform.form.errors['password2'],
- ["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"')
- 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.assertTrue(new_user.has_usable_password())
- def test_user_permission_performance(self):
- u = User.objects.all()[0]
- # Don't depend on a warm cache, see #17377.
- ContentType.objects.clear_cache()
- with self.assertNumQueries(10):
- response = self.client.get('/test_admin/admin/auth/user/%s/' % u.pk)
- self.assertEqual(response.status_code, 200)
- def test_form_url_present_in_context(self):
- u = User.objects.all()[0]
- response = self.client.get('/test_admin/admin3/auth/user/%s/password/' % u.pk)
- self.assertEqual(response.status_code, 200)
- self.assertEqual(response.context['form_url'], 'pony')
- @override_settings(PASSWORD_HASHERS=('django.contrib.auth.hashers.SHA1PasswordHasher',),
- ROOT_URLCONF="admin_views.urls")
- class GroupAdminTest(TestCase):
- """
- Tests group CRUD functionality.
- """
- fixtures = ['admin-views-users.xml']
- def setUp(self):
- self.client.login(username='super', password='secret')
- def test_save_button(self):
- group_count = Group.objects.count()
- response = self.client.post('/test_admin/admin/auth/group/add/', {
- 'name': 'newgroup',
- })
- Group.objects.order_by('-id')[0]
- self.assertRedirects(response, '/test_admin/admin/auth/group/')
- self.assertEqual(Group.objects.count(), group_count + 1)
- def test_group_permission_performance(self):
- g = Group.objects.create(name="test_group")
- # Ensure no queries are skipped due to cached content type for Group.
- ContentType.objects.clear_cache()
- with self.assertNumQueries(8):
- response = self.client.get('/test_admin/admin/auth/group/%s/' % g.pk)
- self.assertEqual(response.status_code, 200)
- @override_settings(PASSWORD_HASHERS=('django.contrib.auth.hashers.SHA1PasswordHasher',),
- ROOT_URLCONF="admin_views.urls")
- class CSSTest(TestCase):
- fixtures = ['admin-views-users.xml']
- def setUp(self):
- self.client.login(username='super', password='secret')
- def test_field_prefix_css_classes(self):
- """
- Ensure that fields have a CSS class name with a 'field-' prefix.
- Refs #16371.
- """
- response = self.client.get('/test_admin/admin/admin_views/post/add/')
- # The main form
- self.assertContains(response, 'class="form-row field-title"')
- self.assertContains(response, 'class="form-row field-content"')
- self.assertContains(response, 'class="form-row field-public"')
- self.assertContains(response, 'class="form-row field-awesomeness_level"')
- self.assertContains(response, 'class="form-row field-coolness"')
- self.assertContains(response, 'class="form-row field-value"')
- self.assertContains(response, 'class="form-row"') # The lambda function
- # The tabular inline
- self.assertContains(response, '<td class="field-url">')
- self.assertContains(response, '<td class="field-posted">')
- def test_index_css_classes(self):
- """
- Ensure that CSS class names are used for each app and model on the
- admin index pages.
- Refs #17050.
- """
- # General index page
- response = self.client.get("/test_admin/admin/")
- self.assertContains(response, '<div class="app-admin_views module">')
- self.assertContains(response, '<tr class="model-actor">')
- self.assertContains(response, '<tr class="model-album">')
- # App index page
- response = self.client.get("/test_admin/admin/admin_views/")
- self.assertContains(response, '<div class="app-admin_views module">')
- self.assertContains(response, '<tr class="model-actor">')
- self.assertContains(response, '<tr class="model-album">')
- def test_app_model_in_form_body_class(self):
- """
- Ensure app and model tag are correctly read by change_form template
- """
- response = self.client.get('/test_admin/admin/admin_views/section/add/')
- self.assertEqual(response.status_code, 200)
- self.assertContains(response,
- '<body class=" app-admin_views model-section ')
- def test_app_model_in_list_body_class(self):
- """
- Ensure app and model tag are correctly read by change_list template
- """
- response = self.client.get('/test_admin/admin/admin_views/section/')
- self.assertEqual(response.status_code, 200)
- self.assertContains(response,
- '<body class=" app-admin_views model-section ')
- def test_app_model_in_delete_confirmation_body_class(self):
- """
- Ensure app and model tag are correctly read by delete_confirmation
- template
- """
- response = self.client.get(
- '/test_admin/admin/admin_views/section/1/delete/')
- self.assertEqual(response.status_code, 200)
- self.assertContains(response,
- '<body class=" app-admin_views model-section ')
- def test_app_model_in_app_index_body_class(self):
- """
- Ensure app and model tag are correctly read by app_index template
- """
- response = self.client.get('/test_admin/admin/admin_views/')
- self.assertEqual(response.status_code, 200)
- self.assertContains(response, '<body class=" dashboard app-admin_views')
- def test_app_model_in_delete_selected_confirmation_body_class(self):
- """
- Ensure app and model tag are correctly read by
- delete_selected_confirmation template
- """
- action_data = {
- ACTION_CHECKBOX_NAME: [1],
- 'action': 'delete_selected',
- 'index': 0,
- }
- response = self.client.post('/test_admin/admin/admin_views/section/',
- action_data)
- self.assertEqual(response.status_code, 200)
- self.assertContains(response,
- '<body class=" app-admin_views model-section ')
- def test_changelist_field_classes(self):
- """
- Cells of the change list table should contain the field name in their class attribute
- Refs #11195.
- """
- Podcast.objects.create(name="Django Dose",
- release_date=datetime.date.today())
- response = self.client.get('/test_admin/admin/admin_views/podcast/')
- self.assertContains(
- response, '<th class="field-name">')
- self.assertContains(
- response, '<td class="field-release_date nowrap">')
- self.assertContains(
- response, '<td class="action-checkbox">')
- try:
- import docutils
- except ImportError:
- docutils = None
- @unittest.skipUnless(docutils, "no docutils installed.")
- @override_settings(PASSWORD_HASHERS=('django.contrib.auth.hashers.SHA1PasswordHasher',),
- ROOT_URLCONF="admin_views.urls")
- @modify_settings(INSTALLED_APPS={'append': ['django.contrib.admindocs', 'django.contrib.flatpages']})
- class AdminDocsTest(TestCase):
- fixtures = ['admin-views-users.xml']
- def setUp(self):
- self.client.login(username='super', password='secret')
- def test_tags(self):
- response = self.client.get('/test_admin/admin/doc/tags/')
- # The builtin tag group exists
- self.assertContains(response, "<h2>Built-in tags</h2>", count=2, html=True)
- # A builtin tag exists in both the index and detail
- self.assertContains(response, '<h3 id="built_in-autoescape">autoescape</h3>', html=True)
- self.assertContains(response, '<li><a href="#built_in-autoescape">autoescape</a></li>', html=True)
- # An app tag exists in both the index and detail
- self.assertContains(response, '<h3 id="flatpages-get_flatpages">get_flatpages</h3>', html=True)
- self.assertContains(response, '<li><a href="#flatpages-get_flatpages">get_flatpages</a></li>', html=True)
- # The admin list tag group exists
- self.assertContains(response, "<h2>admin_list</h2>", count=2, html=True)
- # An admin list tag exists in both the index and detail
- self.assertContains(response, '<h3 id="admin_list-admin_actions">admin_actions</h3>', html=True)
- self.assertContains(response, '<li><a href="#admin_list-admin_actions">admin_actions</a></li>', html=True)
- def test_filters(self):
- response = self.client.get('/test_admin/admin/doc/filters/')
- # The builtin filter group exists
- self.assertContains(response, "<h2>Built-in filters</h2>", count=2, html=True)
- # A builtin filter exists in both the index and detail
- self.assertContains(response, '<h3 id="built_in-add">add</h3>', html=True)
- self.assertContains(response, '<li><a href="#built_in-add">add</a></li>', html=True)
- @override_settings(PASSWORD_HASHERS=('django.contrib.auth.hashers.SHA1PasswordHasher',),
- ROOT_URLCONF="admin_views.urls")
- class ValidXHTMLTests(TestCase):
- fixtures = ['admin-views-users.xml']
- urlbit = 'admin'
- def setUp(self):
- self.client.login(username='super', password='secret')
- @override_settings(
- TEMPLATE_CONTEXT_PROCESSORS=filter(
- lambda t: t != 'django.template.context_processors.i18n',
- global_settings.TEMPLATE_CONTEXT_PROCESSORS),
- USE_I18N=False,
- )
- def test_lang_name_present(self):
- response = self.client.get('/test_admin/%s/admin_views/' % self.urlbit)
- self.assertNotContains(response, ' lang=""')
- self.assertNotContains(response, ' xml:lang=""')
- @override_settings(PASSWORD_HASHERS=('django.contrib.auth.hashers.SHA1PasswordHasher',),
- ROOT_URLCONF="admin_views.urls",
- USE_THOUSAND_SEPARATOR=True, USE_L10N=True)
- class DateHierarchyTests(TestCase):
- fixtures = ['admin-views-users.xml']
- def setUp(self):
- self.client.login(username='super', password='secret')
- def tearDown(self):
- 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__month=%d&release_date__year=%d"' % (
- date.month, date.year))
- def assert_contains_day_link(self, response, date):
- self.assertContains(
- response, '?release_date__day=%d&'
- 'release_date__month=%d&release_date__year=%d"' % (
- date.day, date.month, date.year))
- 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)
- # no day-level links
- 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'))
- # no day/month-level links
- self.assertNotContains(response, 'release_date__day=')
- self.assertNotContains(response, 'release_date__month=')
- for date in DATES:
- self.assert_contains_year_link(response, date)
- # and make sure GET parameters still behave correctly
- 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)
- @override_settings(PASSWORD_HASHERS=('django.contrib.auth.hashers.SHA1PasswordHasher',),
- ROOT_URLCONF="admin_views.urls")
- class AdminCustomSaveRelatedTests(TestCase):
- """
- Ensure that one can easily customize the way related objects are saved.
- Refs #16115.
- """
- fixtures = ['admin-views-users.xml']
- def setUp(self):
- self.client.login(username='super', password='secret')
- def test_should_be_able_to_edit_related_objects_on_add_view(self):
- post = {
- 'child_set-TOTAL_FORMS': '3',
- 'child_set-INITIAL_FORMS': '0',
- 'name': 'Josh Stone',
- 'child_set-0-name': 'Paul',
- 'child_set-1-name': 'Catherine',
- }
- self.client.post('/test_admin/admin/admin_views/parent/add/', post)
- self.assertEqual(1, Parent.objects.count())
- self.assertEqual(2, Child.objects.count())
- children_names = list(Child.objects.order_by('name').values_list('name', flat=True))
- self.assertEqual('Josh Stone', Parent.objects.latest('id').name)
- self.assertEqual(['Catherine Stone', 'Paul Stone'], children_names)
- def test_should_be_able_to_edit_related_objects_on_change_view(self):
- parent = Parent.objects.create(name='Josh Stone')
- paul = Child.objects.create(parent=parent, name='Paul')
- catherine = Child.objects.create(parent=parent, name='Catherine')
- post = {
- 'child_set-TOTAL_FORMS': '5',
- 'child_set-INITIAL_FORMS': '2',
- 'name': 'Josh Stone',
- 'child_set-0-name': 'Paul',
- 'child_set-0-id': paul.id,
- 'child_set-1-name': 'Catherine',
- 'child_set-1-id': catherine.id,
- }
- self.client.post('/test_admin/admin/admin_views/parent/%s/' % parent.id, post)
- children_names = list(Child.objects.order_by('name').values_list('name', flat=True))
- self.assertEqual('Josh Stone', Parent.objects.latest('id').name)
- self.assertEqual(['Catherine Stone', 'Paul Stone'], children_names)
- def test_should_be_able_to_edit_related_objects_on_changelist_view(self):
- parent = Parent.objects.create(name='Josh Rock')
- Child.objects.create(parent=parent, name='Paul')
- Child.objects.create(parent=parent, name='Catherine')
- post = {
- 'form-TOTAL_FORMS': '1',
- 'form-INITIAL_FORMS': '1',
- 'form-MAX_NUM_FORMS': '0',
- 'form-0-id': parent.id,
- 'form-0-name': 'Josh Stone',
- '_save': 'Save'
- }
- self.client.post('/test_admin/admin/admin_views/parent/', post)
- children_names = list(Child.objects.order_by('name').values_list('name', flat=True))
- self.assertEqual('Josh Stone', Parent.objects.latest('id').name)
- self.assertEqual(['Catherine Stone', 'Paul Stone'], children_names)
- @override_settings(PASSWORD_HASHERS=('django.contrib.auth.hashers.SHA1PasswordHasher',),
- ROOT_URLCONF="admin_views.urls")
- class AdminViewLogoutTest(TestCase):
- fixtures = ['admin-views-users.xml']
- def setUp(self):
- self.client.login(username='super', password='secret')
- def test_client_logout_url_can_be_used_to_login(self):
- response = self.client.get('/test_admin/admin/logout/')
- self.assertEqual(response.status_code, 200)
- self.assertTemplateUsed(response, 'registration/logged_out.html')
- self.assertEqual(response.request['PATH_INFO'], '/test_admin/admin/logout/')
- # we are now logged out
- response = self.client.get('/test_admin/admin/logout/')
- self.assertEqual(response.status_code, 302) # we should be redirected to the login page.
- # follow the redirect and test results.
- response = self.client.get('/test_admin/admin/logout/', follow=True)
- self.assertEqual(response.status_code, 200)
- self.assertTemplateUsed(response, 'admin/login.html')
- self.assertEqual(response.request['PATH_INFO'], '/test_admin/admin/login/')
- self.assertContains(response, '<input type="hidden" name="next" value="/test_admin/admin/" />')
- @override_settings(PASSWORD_HASHERS=('django.contrib.auth.hashers.SHA1PasswordHasher',),
- ROOT_URLCONF="admin_views.urls")
- class AdminUserMessageTest(TestCase):
- fixtures = ['admin-views-users.xml']
- def setUp(self):
- self.client.login(username='super', password='secret')
- def send_message(self, level):
- """
- Helper that sends a post to the dummy test methods and asserts that a
- message with the level has appeared in the response.
- """
- action_data = {
- ACTION_CHECKBOX_NAME: [1],
- 'action': 'message_%s' % level,
- 'index': 0,
- }
- response = self.client.post('/test_admin/admin/admin_views/usermessenger/',
- action_data, follow=True)
- self.assertContains(response,
- '<li class="%s">Test %s</li>' % (level, level),
- html=True)
- @override_settings(MESSAGE_LEVEL=10) # Set to DEBUG for this request
- def test_message_debug(self):
- self.send_message('debug')
- def test_message_info(self):
- self.send_message('info')
- def test_message_success(self):
- self.send_message('success')
- def test_message_warning(self):
- self.send_message('warning')
- def test_message_error(self):
- self.send_message('error')
- def test_message_extra_tags(self):
- action_data = {
- ACTION_CHECKBOX_NAME: [1],
- 'action': 'message_extra_tags',
- 'index': 0,
- }
- response = self.client.post('/test_admin/admin/admin_views/usermessenger/',
- action_data, follow=True)
- self.assertContains(response,
- '<li class="extra_tag info">Test tags</li>',
- html=True)
- @override_settings(PASSWORD_HASHERS=('django.contrib.auth.hashers.SHA1PasswordHasher',),
- ROOT_URLCONF="admin_views.urls")
- class AdminKeepChangeListFiltersTests(TestCase):
- fixtures = ['admin-views-users.xml']
- admin_site = site
- def setUp(self):
- self.client.login(username='super', password='secret')
- def assertURLEqual(self, url1, url2):
- """
- Assert that two URLs are equal despite the ordering
- of their querystring. Refs #22360.
- """
- parsed_url1 = urlparse(url1)
- path1 = parsed_url1.path
- parsed_qs1 = dict(parse_qsl(parsed_url1.query))
- parsed_url2 = urlparse(url2)
- path2 = parsed_url2.path
- parsed_qs2 = dict(parse_qsl(parsed_url2.query))
- for parsed_qs in [parsed_qs1, parsed_qs2]:
- if '_changelist_filters' in parsed_qs:
- changelist_filters = parsed_qs['_changelist_filters']
- parsed_filters = dict(parse_qsl(changelist_filters))
- parsed_qs['_changelist_filters'] = parsed_filters
- self.assertEqual(path1, path2)
- self.assertEqual(parsed_qs1, parsed_qs2)
- def test_assert_url_equal(self):
- # Test equality.
- self.assertURLEqual(
- 'http://testserver/test_admin/admin/auth/user/105/?_changelist_filters=is_staff__exact%3D0%26is_superuser__exact%3D0',
- 'http://testserver/test_admin/admin/auth/user/105/?_changelist_filters=is_staff__exact%3D0%26is_superuser__exact%3D0'
- )
- # Test inequality.
- with self.assertRaises(AssertionError):
- self.assertURLEqual(
- 'http://testserver/test_admin/admin/auth/user/105/?_changelist_filters=is_staff__exact%3D0%26is_superuser__exact%3D0',
- 'http://testserver/test_admin/admin/auth/user/105/?_changelist_filters=is_staff__exact%3D1%26is_superuser__exact%3D1'
- )
- # Ignore scheme and host.
- self.assertURLEqual(
- 'http://testserver/test_admin/admin/auth/user/105/?_changelist_filters=is_staff__exact%3D0%26is_superuser__exact%3D0',
- '/test_admin/admin/auth/user/105/?_changelist_filters=is_staff__exact%3D0%26is_superuser__exact%3D0'
- )
- # Ignore ordering of querystring.
- self.assertURLEqual(
- '/test_admin/admin/auth/user/?is_staff__exact=0&is_superuser__exact=0',
- '/test_admin/admin/auth/user/?is_superuser__exact=0&is_staff__exact=0'
- )
- # Ignore ordering of _changelist_filters.
- self.assertURLEqual(
- '/test_admin/admin/auth/user/105/?_changelist_filters=is_staff__exact%3D0%26is_superuser__exact%3D0',
- '/test_admin/admin/auth/user/105/?_changelist_filters=is_superuser__exact%3D0%26is_staff__exact%3D0'
- )
- def get_changelist_filters(self):
- return {
- 'is_superuser__exact': 0,
- 'is_staff__exact': 0,
- }
- def get_changelist_filters_querystring(self):
- return urlencode(self.get_changelist_filters())
- def get_preserved_filters_querystring(self):
- return urlencode({
- '_changelist_filters': self.get_changelist_filters_querystring()
- })
- def get_sample_user_id(self):
- return 104
- def get_changelist_url(self):
- return '%s?%s' % (
- reverse('admin:auth_user_changelist',
- current_app=self.admin_site.name),
- self.get_changelist_filters_querystring(),
- )
- def get_add_url(self):
- return '%s?%s' % (
- reverse('admin:auth_user_add',
- current_app=self.admin_site.name),
- self.get_preserved_filters_querystring(),
- )
- def get_change_url(self, user_id=None):
- if user_id is None:
- user_id = self.get_sample_user_id()
- return "%s?%s" % (
- reverse('admin:auth_user_change', args=(user_id,),
- current_app=self.admin_site.name),
- self.get_preserved_filters_querystring(),
- )
- def get_history_url(self, user_id=None):
- if user_id is None:
- user_id = self.get_sample_user_id()
- return "%s?%s" % (
- reverse('admin:auth_user_history', args=(user_id,),
- current_app=self.admin_site.name),
- self.get_preserved_filters_querystring(),
- )
- def get_delete_url(self, user_id=None):
- if user_id is None:
- user_id = self.get_sample_user_id()
- return "%s?%s" % (
- reverse('admin:auth_user_delete', args=(user_id,),
- current_app=self.admin_site.name),
- self.get_preserved_filters_querystring(),
- )
- def test_changelist_view(self):
- response = self.client.get(self.get_changelist_url())
- self.assertEqual(response.status_code, 200)
- # Check the `change_view` link has the correct querystring.
- detail_link = re.search(
- '<a href="(.*?)">joepublic</a>',
- force_text(response.content)
- )
- self.assertURLEqual(detail_link.group(1), self.get_change_url())
- def test_change_view(self):
- # Get the `change_view`.
- response = self.client.get(self.get_change_url())
- self.assertEqual(response.status_code, 200)
- # Check the form action.
- form_action = re.search(
- '<form enctype="multipart/form-data" action="(.*?)" method="post" id="user_form".*?>',
- force_text(response.content)
- )
- self.assertURLEqual(form_action.group(1), '?%s' % self.get_preserved_filters_querystring())
- # Check the history link.
- history_link = re.search(
- '<a href="(.*?)" class="historylink">History</a>',
- force_text(response.content)
- )
- self.assertURLEqual(history_link.group(1), self.get_history_url())
- # Check the delete link.
- delete_link = re.search(
- '<a href="(.*?)" class="deletelink">Delete</a>',
- force_text(response.content)
- )
- self.assertURLEqual(delete_link.group(1), self.get_delete_url())
- # Test redirect on "Save".
- post_data = {
- 'username': 'joepublic',
- 'last_login_0': '2007-05-30',
- 'last_login_1': '13:20:10',
- 'date_joined_0': '2007-05-30',
- 'date_joined_1': '13:20:10',
- }
- post_data['_save'] = 1
- response = self.client.post(self.get_change_url(), data=post_data)
- self.assertEqual(response.status_code, 302)
- self.assertURLEqual(
- response.url,
- self.get_changelist_url()
- )
- post_data.pop('_save')
- # Test redirect on "Save and continue".
- post_data['_continue'] = 1
- response = self.client.post(self.get_change_url(), data=post_data)
- self.assertEqual(response.status_code, 302)
- self.assertURLEqual(
- response.url,
- self.get_change_url()
- )
- post_data.pop('_continue')
- # Test redirect on "Save and add new".
- post_data['_addanother'] = 1
- response = self.client.post(self.get_change_url(), data=post_data)
- self.assertEqual(response.status_code, 302)
- self.assertURLEqual(
- response.url,
- self.get_add_url()
- )
- post_data.pop('_addanother')
- def test_add_view(self):
- # Get the `add_view`.
- response = self.client.get(self.get_add_url())
- self.assertEqual(response.status_code, 200)
- # Check the form action.
- form_action = re.search(
- '<form enctype="multipart/form-data" action="(.*?)" method="post" id="user_form".*?>',
- force_text(response.content)
- )
- self.assertURLEqual(form_action.group(1), '?%s' % self.get_preserved_filters_querystring())
- post_data = {
- 'username': 'dummy',
- 'password1': 'test',
- 'password2': 'test',
- }
- # Test redirect on "Save".
- post_data['_save'] = 1
- response = self.client.post(self.get_add_url(), data=post_data)
- self.assertEqual(response.status_code, 302)
- self.assertURLEqual(
- response.url,
- self.get_change_url(User.objects.latest('pk').pk)
- )
- post_data.pop('_save')
- # Test redirect on "Save and continue".
- post_data['username'] = 'dummy2'
- post_data['_continue'] = 1
- response = self.client.post(self.get_add_url(), data=post_data)
- self.assertEqual(response.status_code, 302)
- self.assertURLEqual(
- response.url,
- self.get_change_url(User.objects.latest('pk').pk)
- )
- post_data.pop('_continue')
- # Test redirect on "Save and add new".
- post_data['username'] = 'dummy3'
- post_data['_addanother'] = 1
- response = self.client.post(self.get_add_url(), data=post_data)
- self.assertEqual(response.status_code, 302)
- self.assertURLEqual(
- response.url,
- self.get_add_url()
- )
- post_data.pop('_addanother')
- def test_delete_view(self):
- # Test redirect on "Delete".
- response = self.client.post(self.get_delete_url(), {'post': 'yes'})
- self.assertEqual(response.status_code, 302)
- self.assertURLEqual(
- response.url,
- self.get_changelist_url()
- )
- def test_url_prefix(self):
- context = {
- 'preserved_filters': self.get_preserved_filters_querystring(),
- 'opts': User._meta,
- }
- url = reverse('admin:auth_user_changelist', current_app=self.admin_site.name)
- self.assertURLEqual(
- self.get_changelist_url(),
- add_preserved_filters(context, url),
- )
- original_prefix = get_script_prefix()
- try:
- set_script_prefix('/prefix/')
- url = reverse('admin:auth_user_changelist', current_app=self.admin_site.name)
- self.assertURLEqual(
- self.get_changelist_url(),
- add_preserved_filters(context, url),
- )
- finally:
- set_script_prefix(original_prefix)
- class NamespacedAdminKeepChangeListFiltersTests(AdminKeepChangeListFiltersTests):
- admin_site = site2
- @override_settings(PASSWORD_HASHERS=('django.contrib.auth.hashers.SHA1PasswordHasher',),
- ROOT_URLCONF="admin_views.urls")
- class TestLabelVisibility(TestCase):
- """ #11277 -Labels of hidden fields in admin were not hidden. """
- fixtures = ['admin-views-users.xml']
- def setUp(self):
- self.client.login(username='super', password='secret')
- def test_all_fields_visible(self):
- response = self.client.get('/test_admin/admin/admin_views/emptymodelvisible/add/')
- self.assert_fieldline_visible(response)
- self.assert_field_visible(response, 'first')
- self.assert_field_visible(response, 'second')
- def test_all_fields_hidden(self):
- response = self.client.get('/test_admin/admin/admin_views/emptymodelhidden/add/')
- self.assert_fieldline_hidden(response)
- self.assert_field_hidden(response, 'first')
- self.assert_field_hidden(response, 'second')
- def test_mixin(self):
- response = self.client.get('/test_admin/admin/admin_views/emptymodelmixin/add/')
- self.assert_fieldline_visible(response)
- self.assert_field_hidden(response, 'first')
- self.assert_field_visible(response, 'second')
- def assert_field_visible(self, response, field_name):
- self.assertContains(response, '<div class="field-box field-%s">' % field_name)
- def assert_field_hidden(self, response, field_name):
- self.assertContains(response, '<div class="field-box field-%s hidden">' % field_name)
- def assert_fieldline_visible(self, response):
- self.assertContains(response, '<div class="form-row field-first field-second">')
- def assert_fieldline_hidden(self, response):
- self.assertContains(response, '<div class="form-row hidden')
- @override_settings(PASSWORD_HASHERS=('django.contrib.auth.hashers.SHA1PasswordHasher',),
- ROOT_URLCONF="admin_views.urls")
- class AdminViewOnSiteTests(TestCase):
- fixtures = ['admin-views-users.xml', 'admin-views-restaurants.xml']
- def setUp(self):
- self.client.login(username='super', password='secret')
- def test_add_view_form_and_formsets_run_validation(self):
- """
- Issue #20522
- Verifying that if the parent form fails validation, the inlines also
- run validation even if validation is contingent on parent form data
- """
- # The form validation should fail because 'some_required_info' is
- # not included on the parent form, and the family_name of the parent
- # does not match that of the child
- post_data = {"family_name": "Test1",
- "dependentchild_set-TOTAL_FORMS": "1",
- "dependentchild_set-INITIAL_FORMS": "0",
- "dependentchild_set-MAX_NUM_FORMS": "1",
- "dependentchild_set-0-id": "",
- "dependentchild_set-0-parent": "",
- "dependentchild_set-0-family_name": "Test2"}
- response = self.client.post('/test_admin/admin/admin_views/parentwithdependentchildren/add/',
- post_data)
- # just verifying the parent form failed validation, as expected --
- # this isn't the regression test
- self.assertIn('some_required_info', response.context['adminform'].form.errors)
- # actual regression test
- for error_set in response.context['inline_admin_formset'].formset.errors:
- self.assertEqual(['Children must share a family name with their parents in this contrived test case'],
- error_set.get('__all__'))
- def test_change_view_form_and_formsets_run_validation(self):
- """
- Issue #20522
- Verifying that if the parent form fails validation, the inlines also
- run validation even if validation is contingent on parent form data
- """
- pwdc = ParentWithDependentChildren.objects.create(some_required_info=6,
- family_name="Test1")
- # The form validation should fail because 'some_required_info' is
- # not included on the parent form, and the family_name of the parent
- # does not match that of the child
- post_data = {"family_name": "Test2",
- "dependentchild_set-TOTAL_FORMS": "1",
- "dependentchild_set-INITIAL_FORMS": "0",
- "dependentchild_set-MAX_NUM_FORMS": "1",
- "dependentchild_set-0-id": "",
- "dependentchild_set-0-parent": str(pwdc.id),
- "dependentchild_set-0-family_name": "Test1"}
- response = self.client.post('/test_admin/admin/admin_views/parentwithdependentchildren/%d/'
- % pwdc.id, post_data)
- # just verifying the parent form failed validation, as expected --
- # this isn't the regression test
- self.assertIn('some_required_info', response.context['adminform'].form.errors)
- # actual regression test
- for error_set in response.context['inline_admin_formset'].formset.errors:
- self.assertEqual(['Children must share a family name with their parents in this contrived test case'],
- error_set.get('__all__'))
- def test_check(self):
- "Ensure that the view_on_site value is either a boolean or a callable"
- try:
- CityAdmin.view_on_site = True
- self.assertEqual(CityAdmin.check(City), [])
- CityAdmin.view_on_site = False
- self.assertEqual(CityAdmin.check(City), [])
- CityAdmin.view_on_site = lambda obj: obj.get_absolute_url()
- self.assertEqual(CityAdmin.check(City), [])
- CityAdmin.view_on_site = []
- self.assertEqual(CityAdmin.check(City), [
- Error(
- "The value of 'view_on_site' must be a callable or a boolean value.",
- hint=None,
- obj=CityAdmin,
- id='admin.E025',
- ),
- ])
- finally:
- # Restore the original values for the benefit of other tests.
- CityAdmin.view_on_site = True
- def test_false(self):
- "Ensure that the 'View on site' button is not displayed if view_on_site is False"
- response = self.client.get('/test_admin/admin/admin_views/restaurant/1/')
- content_type_pk = ContentType.objects.get_for_model(Restaurant).pk
- self.assertNotContains(response,
- '"/test_admin/admin/r/%s/1/"' % content_type_pk,
- )
- def test_true(self):
- "Ensure that the default behavior is followed if view_on_site is True"
- response = self.client.get('/test_admin/admin/admin_views/city/1/')
- content_type_pk = ContentType.objects.get_for_model(City).pk
- self.assertContains(response,
- '"/test_admin/admin/r/%s/1/"' % content_type_pk,
- )
- def test_callable(self):
- "Ensure that the right link is displayed if view_on_site is a callable"
- response = self.client.get('/test_admin/admin/admin_views/worker/1/')
- worker = Worker.objects.get(pk=1)
- self.assertContains(response,
- '"/worker/%s/%s/"' % (worker.surname, worker.name),
- )
- def test_missing_get_absolute_url(self):
- "Ensure None is returned if model doesn't have get_absolute_url"
- model_admin = ModelAdmin(Worker, None)
- self.assertIsNone(model_admin.get_view_on_site_url(Worker()))
- @override_settings(PASSWORD_HASHERS=('django.contrib.auth.hashers.SHA1PasswordHasher',),
- ROOT_URLCONF="admin_views.urls")
- class InlineAdminViewOnSiteTest(TestCase):
- fixtures = ['admin-views-users.xml', 'admin-views-restaurants.xml']
- def setUp(self):
- self.client.login(username='super', password='secret')
- def test_false(self):
- "Ensure that the 'View on site' button is not displayed if view_on_site is False"
- response = self.client.get('/test_admin/admin/admin_views/state/1/')
- content_type_pk = ContentType.objects.get_for_model(City).pk
- self.assertNotContains(response,
- '/test_admin/admin/r/%s/1/' % content_type_pk,
- )
- def test_true(self):
- "Ensure that the 'View on site' button is displayed if view_on_site is True"
- response = self.client.get('/test_admin/admin/admin_views/city/1/')
- content_type_pk = ContentType.objects.get_for_model(Restaurant).pk
- self.assertContains(response,
- '/test_admin/admin/r/%s/1/' % content_type_pk,
- )
- def test_callable(self):
- "Ensure that the right link is displayed if view_on_site is a callable"
- response = self.client.get('/test_admin/admin/admin_views/restaurant/1/')
- worker = Worker.objects.get(pk=1)
- self.assertContains(response,
- '"/worker_inline/%s/%s/"' % (worker.surname, worker.name),
- )
- class AdminGenericRelationTests(TestCase):
- def test_generic_relation_fk_list_filter(self):
- """
- Validates a model with a generic relation to a model with
- a foreign key can specify the generic+fk relationship
- path as a list_filter. See trac #21428.
- """
- class GenericFKAdmin(ModelAdmin):
- list_filter = ('tags__content_type',)
- validator = ModelAdminValidator()
- try:
- validator.validate_list_filter(GenericFKAdmin, Plot)
- except ImproperlyConfigured:
- self.fail("Couldn't validate a GenericRelation -> FK path in ModelAdmin.list_filter")
- @override_settings(ROOT_URLCONF="admin_views.urls")
- class TestEtagWithAdminView(TestCase):
- # See https://code.djangoproject.com/ticket/16003
- def test_admin(self):
- with self.settings(USE_ETAGS=False):
- response = self.client.get('/test_admin/admin/')
- self.assertEqual(response.status_code, 302)
- self.assertFalse(response.has_header('ETag'))
- with self.settings(USE_ETAGS=True):
- response = self.client.get('/test_admin/admin/')
- self.assertEqual(response.status_code, 302)
- self.assertTrue(response.has_header('ETag'))
- @override_settings(
- PASSWORD_HASHERS=('django.contrib.auth.hashers.SHA1PasswordHasher',),
- ROOT_URLCONF="admin_views.urls",
- )
- class GetFormsetsWithInlinesArgumentTest(TestCase):
- """
- #23934 - When adding a new model instance in the admin, the 'obj' argument
- of get_formsets_with_inlines() should be None. When changing, it should be
- equal to the existing model instance.
- The GetFormsetsArgumentCheckingAdmin ModelAdmin throws an exception
- if obj is not None during add_view or obj is None during change_view.
- """
- fixtures = ['admin-views-users.xml']
- def setUp(self):
- self.client.login(username='super', password='secret')
- def test_explicitly_provided_pk(self):
- post_data = {'name': '1'}
- try:
- response = self.client.post('/test_admin/admin/admin_views/explicitlyprovidedpk/add/', post_data)
- except Exception as e:
- self.fail(e)
- self.assertEqual(response.status_code, 302)
- post_data = {'name': '2'}
- try:
- response = self.client.post('/test_admin/admin/admin_views/explicitlyprovidedpk/1/', post_data)
- except Exception as e:
- self.fail(e)
- self.assertEqual(response.status_code, 302)
- def test_implicitly_generated_pk(self):
- post_data = {'name': '1'}
- try:
- response = self.client.post('/test_admin/admin/admin_views/implicitlygeneratedpk/add/', post_data)
- except Exception as e:
- self.fail(e)
- self.assertEqual(response.status_code, 302)
- post_data = {'name': '2'}
- try:
- response = self.client.post('/test_admin/admin/admin_views/implicitlygeneratedpk/1/', post_data)
- except Exception as e:
- self.fail(e)
- self.assertEqual(response.status_code, 302)
|