Formtillgångar (klassen ”Media”)Link to this heading

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 som en statisk definitionLink to this heading

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:

Code
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:

Python console
>>> 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.

cssLink to this heading

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 avsnittet om sökvägar 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:

Code
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:

Code
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:

Django template
<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">

Stylesheet objectsLink to this heading

class Stylesheet(href, **attributes)Link to this definition

Represents a stylesheet link. The rel attribute is set to "stylesheet".

The first parameter, href, is the string path to the stylesheet file. See the section on paths for details on how to specify paths to these files.

The optional keyword arguments, **attributes, are additional HTML attributes set on the rendered <link> tag.

Se Banor som objekt för användningsexempel.

jsLink to this heading

En tupel som beskriver de JavaScript-filer som krävs. Se avsnittet om sökvägar för information om hur du anger sökvägar till dessa filer.

Script-objektLink to this heading

class Script(src, **attributes)Link to this definition

Representerar en skriptfil.

Den första parametern, src, är strängsökvägen till skriptfilen. Se avsnittet om sökvägar 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ängaLink to this heading

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:

Python console
>>> 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:

Python console
>>> 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 egenskapLink to this heading

If you need to perform some more sophisticated manipulation of asset requirements, you can define the media property directly. This is done by defining a widget property that returns an instance of forms.Media. The constructor for forms.Media accepts css and js keyword arguments in the same format as that used in a static media definition.

Till exempel kan den statiska definitionen för vår Calendar Widget också definieras på ett dynamiskt sätt:

Code
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ångsdefinitionerLink to this heading

Banor som strängarLink to this heading

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:

Python console
>>> 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/':

Python console
>>> 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:

Python console
>>> 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 objektLink to this heading

Assets may also be object-based, using Script or Stylesheet. These allow you to pass custom HTML attributes:

Code
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:

Django template
<script src="https://cdn.example.com/something.min.js"
        crossorigin="anonymous"
        async>
</script>

Similarly, a Stylesheet object can be used to add a stylesheet link with custom HTML attributes:

Code
class Media:
    css = {
        "all": [
            Stylesheet(
                "https://cdn.example.com/print.css",
                crossorigin="anonymous",
                media="print",
            ),
        ]
    }

If this Media definition were to be rendered, it would become:

Django template
<link href="https://cdn.example.com/print.css"
      crossorigin="anonymous"
      media="print"
      rel="stylesheet">

Media objektLink to this heading

class MediaLink to this definition

Represents a collection of media assets required by a widget or form.

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ångarLink to this heading

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:

Python console
>>> 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-objektLink to this heading

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:

Python console
>>> 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öljdLink to this heading

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:

Python console
>>> 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ärLink to this heading

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:

Python console
>>> 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:

Python console
>>> 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>