Formtillgångar (klassen ”Media”)¶
För att skapa ett attraktivt och lättanvänt webbformulär krävs mer än bara HTML - det krävs också CSS-stilmallar, och om du vill använda snygga widgetar kan du också behöva inkludera lite JavaScript på varje sida. Den exakta kombinationen av CSS och JavaScript som krävs för en viss sida beror på vilka widgetar som används på den sidan.
Det är här tillgångsdefinitioner kommer in i bilden. Django låter dig associera olika filer - som stilmallar och skript - med de formulär och widgetar som kräver dessa tillgångar. Om du till exempel vill använda en kalender för att rendera DateFields kan du definiera en anpassad Calendar-widget. Denna widget kan sedan associeras med den CSS och JavaScript som krävs för att rendera kalendern. När kalenderwidgeten används i ett formulär kan Django identifiera de CSS- och JavaScript-filer som krävs och tillhandahålla listan med filnamn i ett formulär som är lämpligt att inkludera på din webbsida.
Tillgångar och Django Admin
Programmet Django Admin definierar ett antal anpassade widgetar för kalendrar, filtrerade val och så vidare. Dessa widgetar definierar tillgångskrav och Django Admin använder de anpassade widgetarna i stället för Djangos standardinställningar. Admin-mallarna innehåller endast de filer som krävs för att rendera widgetarna på en viss sida.
Om du gillar widgetarna som Django Admin-applikationen använder, känn dig fri att använda dem i din egen applikation! De är alla lagrade i django.contrib.admin.widgets
.
Vilken JavaScript-verktygslåda?
Det finns många JavaScript-verktygslådor och många av dem innehåller widgets (t.ex. kalenderwidgets) som kan användas för att förbättra din applikation. Django har medvetet undvikit att välsigna någon JavaScript-verktygssats. Varje verktygslåda har sina egna relativa styrkor och svagheter - använd den verktygslåda som passar dina krav. Django kan integreras med vilken JavaScript-verktygslåda som helst.
Tillgångar som en statisk definition¶
Det enklaste sättet att definiera tillgångar är som en statisk definition. Med den här metoden är deklarationen en inre klass av typen Media
. Egenskaperna för den inre klassen definierar kraven.
Här är ett exempel:
from django import forms
class CalendarWidget(forms.TextInput):
class Media:
css = {
"all": ["pretty.css"],
}
js = ["animations.js", "actions.js"]
Denna kod definierar en CalendarWidget
, som kommer att baseras på TextInput
. Varje gång CalendarWidget används i ett formulär kommer formuläret att instrueras att inkludera CSS-filen pretty.css
och JavaScript-filerna animations.js
och actions.js
.
Denna statiska definition omvandlas vid körning till en widgetegenskap med namnet media
. Listan över tillgångar för en CalendarWidget
-instans kan hämtas via den här egenskapen:
>>> w = CalendarWidget()
>>> print(w.media)
<link href="https://static.example.com/pretty.css" media="all" rel="stylesheet">
<script src="https://static.example.com/animations.js"></script>
<script src="https://static.example.com/actions.js"></script>
Här är en lista över alla möjliga Media
-alternativ. Det finns inga obligatoriska alternativ.
css
¶
En ordbok som beskriver de CSS-filer som krävs för olika typer av utdatamedia.
Värdena i ordlistan bör vara en tupel/lista med filnamn. Se :ref:``avsnittet om sökvägar <form-asset-paths>` för information om hur du anger sökvägar till dessa filer.
Nycklarna i ordlistan är mediatyperna för utdata. Dessa är samma typer som accepteras av CSS-filer i mediedeklarationer: ’all’, ’aural’, ’braille’, ’embossed’, ’handheld’, ’print’, ’projection’, ’screen’, ’tty’ och ’tv’. Om du behöver ha olika stilmallar för olika medietyper ska du ange en lista med CSS-filer för varje utdatamedium. Följande exempel skulle ge två CSS-alternativ - ett för skärmen och ett för utskrift:
class Media:
css = {
"screen": ["pretty.css"],
"print": ["newspaper.css"],
}
Om en grupp CSS-filer är lämpliga för flera olika typer av utdatamedier kan ordboksnyckeln vara en kommaseparerad lista över typerna av utdatamedier. I följande exempel kommer TV-apparater och projektorer att ha samma mediekrav:
class Media:
css = {
"screen": ["pretty.css"],
"tv,projector": ["lo_res.css"],
"print": ["newspaper.css"],
}
Om denna sista CSS-definition skulle renderas skulle den bli följande HTML:
<link href="https://static.example.com/pretty.css" media="screen" rel="stylesheet">
<link href="https://static.example.com/lo_res.css" media="tv,projector" rel="stylesheet">
<link href="https://static.example.com/newspaper.css" media="print" rel="stylesheet">
js
¶
En tupel som beskriver de JavaScript-filer som krävs. Se :ref:``avsnittet om sökvägar <form-asset-paths>` för information om hur du anger sökvägar till dessa filer.
Script
-objekt¶
- class Script(src, **attributes)[source]¶
Representerar en skriptfil.
Den första parametern,
src
, är strängsökvägen till skriptfilen. Se :ref:``avsnittet om sökvägar <form-asset-paths>` för mer information om hur du anger sökvägar till dessa filer.De valfria nyckelordsargumenten,
**attributes
, är HTML-attribut som ställs in på den återgivna<script>
-taggen.Se Banor som objekt för användningsexempel.
förlänga
¶
En boolean som definierar arvsbeteendet för Media
-deklarationer.
Som standard kommer alla objekt som använder en statisk Media
-definition att ärva alla tillgångar som är associerade med den överordnade widgeten. Detta sker oavsett hur den överordnade widgeten definierar sina egna krav. Till exempel, om vi skulle utöka vår grundläggande kalenderwidget från exemplet ovan:
>>> class FancyCalendarWidget(CalendarWidget):
... class Media:
... css = {
... "all": ["fancy.css"],
... }
... js = ["whizbang.js"]
...
>>> w = FancyCalendarWidget()
>>> print(w.media)
<link href="https://static.example.com/pretty.css" media="all" rel="stylesheet">
<link href="https://static.example.com/fancy.css" media="all" rel="stylesheet">
<script src="https://static.example.com/animations.js"></script>
<script src="https://static.example.com/actions.js"></script>
<script src="https://static.example.com/whizbang.js"></script>
FancyCalendar-widgeten ärver alla tillgångar från sin föräldrawidget. Om du inte vill att Media
ska ärvas på detta sätt lägger du till en extend=False
-deklaration i Media
-deklarationen:
>>> class FancyCalendarWidget(CalendarWidget):
... class Media:
... extend = False
... css = {
... "all": ["fancy.css"],
... }
... js = ["whizbang.js"]
...
>>> w = FancyCalendarWidget()
>>> print(w.media)
<link href="https://static.example.com/fancy.css" media="all" rel="stylesheet">
<script src="https://static.example.com/whizbang.js"></script>
Om du vill ha ännu mer kontroll över arvet kan du definiera dina tillgångar med hjälp av en dynamisk egenskap. Dynamiska egenskaper ger dig fullständig kontroll över vilka filer som ärvs och vilka som inte ärvs.
Media
som en dynamisk egenskap¶
Om du behöver utföra mer sofistikerad manipulation av tillgångskrav kan du definiera egenskapen media
direkt. Detta görs genom att definiera en widgetegenskap som returnerar en instans av forms.Media
. Konstruktören för forms.Media
accepterar css
och js
nyckelordsargument i samma format som används i en statisk mediadefinition.
Till exempel kan den statiska definitionen för vår Calendar Widget också definieras på ett dynamiskt sätt:
class CalendarWidget(forms.TextInput):
@property
def media(self):
return forms.Media(
css={"all": ["pretty.css"]}, js=["animations.js", "actions.js"]
)
Se avsnittet om Mediaobjekt för mer information om hur man konstruerar returvärden för dynamiska media
-egenskaper.
Vägar i tillgångsdefinitioner¶
Banor som strängar¶
Strängsökvägar som används för att ange tillgångar kan vara antingen relativa eller absoluta. Om en sökväg börjar med /
, http://
eller https://
kommer den att tolkas som en absolut sökväg och lämnas som den är. Alla andra sökvägar kommer att föregås av värdet på lämpligt prefix. Om appen django.contrib.staticfiles
är installerad kommer den att användas för att servera tillgångar.
Oavsett om du använder django.contrib.staticfiles
eller inte, krävs inställningarna STATIC_URL
och STATIC_ROOT
för att rendera en komplett webbsida.
För att hitta det lämpliga prefixet att använda kommer Django att kontrollera om inställningen STATIC_URL
inte är None
och automatiskt falla tillbaka till att använda MEDIA_URL
. Till exempel, om MEDIA_URL
för din webbplats var 'https://uploads.example.com/'
och STATIC_URL
var None
:
>>> from django import forms
>>> class CalendarWidget(forms.TextInput):
... class Media:
... css = {
... "all": ["/css/pretty.css"],
... }
... js = ["animations.js", "https://othersite.com/actions.js"]
...
>>> w = CalendarWidget()
>>> print(w.media)
<link href="/css/pretty.css" media="all" rel="stylesheet">
<script src="https://uploads.example.com/animations.js"></script>
<script src="https://othersite.com/actions.js"></script>
Men om STATIC_URL
är 'https://static.example.com/'
:
>>> w = CalendarWidget()
>>> print(w.media)
<link href="/css/pretty.css" media="all" rel="stylesheet">
<script src="https://static.example.com/animations.js"></script>
<script src="https://othersite.com/actions.js"></script>
Eller om staticfiles
konfigureras med hjälp av ManifestStaticFilesStorage
:
>>> w = CalendarWidget()
>>> print(w.media)
<link href="/css/pretty.css" media="all" rel="stylesheet">
<script src="https://static.example.com/animations.27e20196a850.js"></script>
<script src="https://othersite.com/actions.js"></script>
Banor som objekt¶
Tillgångar kan också vara objektbaserade, med Script
. Dessutom tillåter dessa att du skickar anpassade HTML-attribut:
class Media:
js = [
Script(
"https://cdn.example.com/something.min.js",
**{
"crossorigin": "anonymous",
"async": True,
},
),
]
Om denna Media-definition skulle renderas, skulle den bli följande HTML:
<script src="https://cdn.example.com/something.min.js"
crossorigin="anonymous"
async>
</script>
Objektklassen Script
lades till.
Media
objekt¶
När du frågar efter media
-attributet för en widget eller ett formulär, returneras ett forms.Media
-objekt. Som vi redan har sett är strängrepresentationen av ett Media
-objekt den HTML som krävs för att inkludera de relevanta filerna i <head>
-blocket på din HTML-sida.
Objekt av typen Media
har dock några andra intressanta egenskaper.
Delmängder av tillgångar¶
Om du bara vill ha filer av en viss typ kan du använda operatorn subscript för att filtrera ut ett medium av intresse. Till exempel:
>>> w = CalendarWidget()
>>> print(w.media)
<link href="https://static.example.com/pretty.css" media="all" rel="stylesheet">
<script src="https://static.example.com/animations.js"></script>
<script src="https://static.example.com/actions.js"></script>
>>> print(w.media["css"])
<link href="https://static.example.com/pretty.css" media="all" rel="stylesheet">
När du använder subscript-operatorn returneras ett nytt Media
-objekt - men ett som bara innehåller de medier som är av intresse.
Kombinera Media
-objekt¶
Media
-objekt kan också läggas till tillsammans. När två Media
-objekt läggs till innehåller det resulterande Media
-objektet en sammanslagning av de tillgångar som anges av båda:
>>> from django import forms
>>> class CalendarWidget(forms.TextInput):
... class Media:
... css = {
... "all": ["pretty.css"],
... }
... js = ["animations.js", "actions.js"]
...
>>> class OtherWidget(forms.TextInput):
... class Media:
... js = ["whizbang.js"]
...
>>> w1 = CalendarWidget()
>>> w2 = OtherWidget()
>>> print(w1.media + w2.media)
<link href="https://static.example.com/pretty.css" media="all" rel="stylesheet">
<script src="https://static.example.com/animations.js"></script>
<script src="https://static.example.com/actions.js"></script>
<script src="https://static.example.com/whizbang.js"></script>
Tillgångarnas ordningsföljd¶
Den ordning i vilken tillgångar infogas i DOM är ofta viktig. Du kan t.ex. ha ett skript som är beroende av jQuery. Genom att kombinera Media
-objekt försöker man därför bevara den relativa ordning i vilken tillgångarna definieras i varje Media
-klass.
Till exempel:
>>> from django import forms
>>> class CalendarWidget(forms.TextInput):
... class Media:
... js = ["jQuery.js", "calendar.js", "noConflict.js"]
...
>>> class TimeWidget(forms.TextInput):
... class Media:
... js = ["jQuery.js", "time.js", "noConflict.js"]
...
>>> w1 = CalendarWidget()
>>> w2 = TimeWidget()
>>> print(w1.media + w2.media)
<script src="https://static.example.com/jQuery.js"></script>
<script src="https://static.example.com/calendar.js"></script>
<script src="https://static.example.com/time.js"></script>
<script src="https://static.example.com/noConflict.js"></script>
Om du kombinerar Media
-objekt med tillgångar i en motstridig ordning resulterar det i en MediaOrderConflictWarning
.
Media
på formulär¶
Widgets är inte de enda objekt som kan ha media
-definitioner - även formulär kan definiera media
. Reglerna för media
-definitioner på formulär är desamma som för widgets: deklarationer kan vara statiska eller dynamiska; sökvägs- och arvsreglerna för dessa deklarationer är exakt desamma.
Oavsett om du definierar en media
-deklaration har alla formulärobjekt en media
-egenskap. Standardvärdet för den här egenskapen är resultatet av att lägga till media
-definitionerna för alla widgetar som ingår i formuläret:
>>> from django import forms
>>> class ContactForm(forms.Form):
... date = DateField(widget=CalendarWidget)
... name = CharField(max_length=40, widget=OtherWidget)
...
>>> f = ContactForm()
>>> f.media
<link href="https://static.example.com/pretty.css" media="all" rel="stylesheet">
<script src="https://static.example.com/animations.js"></script>
<script src="https://static.example.com/actions.js"></script>
<script src="https://static.example.com/whizbang.js"></script>
Om du vill koppla ytterligare tillgångar till ett formulär, t.ex. CSS för formulärlayout, kan du lägga till en Media
-deklaration i formuläret:
>>> class ContactForm(forms.Form):
... date = DateField(widget=CalendarWidget)
... name = CharField(max_length=40, widget=OtherWidget)
... class Media:
... css = {
... "all": ["layout.css"],
... }
...
>>> f = ContactForm()
>>> f.media
<link href="https://static.example.com/pretty.css" media="all" rel="stylesheet">
<link href="https://static.example.com/layout.css" media="all" rel="stylesheet">
<script src="https://static.example.com/animations.js"></script>
<script src="https://static.example.com/actions.js"></script>
<script src="https://static.example.com/whizbang.js"></script>