[Django] Dateien + Bilder speichern

Django, Flask, Bottle, WSGI, CGI…
Antworten
Lasse
User
Beiträge: 112
Registriert: Donnerstag 3. Februar 2011, 18:25

Hallo,
ich möchte in einer mysql Datenbank Bilder und Dokumente speichern (Datentyp: longblob). Die Datenbank besteht bereits, um die Erstellung muss ich mir also keine Sorgen machen.

Hier meine Models:

Code: Alles auswählen

class Image(models.Model): #11
    """Diese Tabelle enthaelt die Bilder fuer Artikel. Fuer jeden
    Artikel koennen mehrere Bilder in gleichen und verschiedenen
    Groessen gespeichert werden.
    """
    class Meta:
        db_table = 'Image'
        verbose_name = 'Bild'
        verbose_name_plural = 'Bilder'
    ID = UUIDField(primary_key=True, version=4)
    ArticleVariant = models.ForeignKey(ArticleVariant)
    Description = models.CharField(max_length=50)
    Attachment = models.ImageField()
    OriginalFileName = models.CharField(max_length=50)
    AttachmentSource = models.ForeignKey(AttachmentSource)
    ImageType = models.ForeignKey(ImageType) #wird automatisch aus Dateiendung abgeleitet
    Width = models.IntegerField() #wird automatisch bestimmt
    Height = models.IntegerField() #wird automatisch bestimmt
    Thumbnail = models.ImageField() #wird automatisch generiert
    Year = models.IntegerField() #wird aus dem Dateinamen abgeleitet

    public = models.BooleanField()
    editor = models.ForeignKey(User)
    timestamp = models.DateTimeField()
    
    def __unicode__(self):
        return self.OriginalFileName

class Document(models.Model): #12
    """Diese Tabelle kann je Artikelvariante mehrere Dokumente aufnehmen."""
    class Meta:
        db_table = 'Document'
        verbose_name = 'Dokument'
        verbose_name_plural = 'Dokumente'
    ArticleVariant = models.ForeignKey(ArticleVariant)
    Description = models.TextField(editable=False) #Automatisch abgeleitet aus Dateiname
    Attachment = models.FileField()
    OriginalFileName = models.CharField(max_length=50, editable=False)
    AttachmentSource = models.ForeignKey(AttachmentSource)
    DocumentType = models.ForeignKey(DocumentType, editable=False) #wird automatisch aus der Dateiendung abgeleitet
    Year = models.IntegerField() #wird automatisch aus dem Dateinamen gebildet

    public = models.BooleanField()
    editor = models.ForeignKey(User)
    timestamp = models.DateTimeField()
    
    def __unicode__(self):
        return self.OriginalFileName
Man soll ja für blobs TextField im Model auswählen allerdings weiß ich nicht ob die für 100MB reichen außerdem will ich sie auch im admin anzeigen.

Gruß
Lasse
Benutzeravatar
/me
User
Beiträge: 3555
Registriert: Donnerstag 25. Juni 2009, 14:40
Wohnort: Bonn

Lasse hat geschrieben:ich möchte in einer mysql Datenbank Bilder und Dokumente speichern (Datentyp: longblob). Die Datenbank besteht bereits, um die Erstellung muss ich mir also keine Sorgen machen.
Doch, musst du. Deine Vorstellung davon wo das Bild gespeichert wird stimmt nämlich nicht. Ein ImageField in Django speichert die Daten nicht in der Datenbank.

Lies mal die Dokumentation zu FileField das die Basis für ImageField darstellt.
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

Warum willst du die Bilder denn in einer MySQL-Datenbank ablegen? Der Ansatz hat merh Nach- als Vorteile. Dein System hat bereits eine für diese Aufgabe eine hochspezialisierte Komponente, die nennt sich Dateisystem.
Das Leben ist wie ein Tennisball.
Lasse
User
Beiträge: 112
Registriert: Donnerstag 3. Februar 2011, 18:25

Das liegt an einer existierenden Datenbankstruktur, mit Daten, die unter keinen Umständen verändert werden darf. (Vorgabe vom Auftraggeber)

@/me das das Image oder Document Field die Sachen im Dateisystem speichert ist mir klar. Ich meine, das Django die Tabellen nicht erstellen braucht.
Benutzeravatar
noisefloor
User
Beiträge: 3856
Registriert: Mittwoch 17. Oktober 2007, 21:40
Wohnort: WW
Kontaktdaten:

Hallo,
allerdings weiß ich nicht ob die für 100MB reichen
Das wissen wir auch nicht. ;-) Wenn's eine bestehende Datenstruktur ist, dann kannst du doch in die DB schauen, wie groß das größte BLOB ist. Ansonsten hol' dir eine Vorgabe vom Auftraggeber. :-)

Gruß, noisefloor
Lasse
User
Beiträge: 112
Registriert: Donnerstag 3. Februar 2011, 18:25

Die Aufgabe ganz konkret:
-Dateien und Bilder über die Admin-Oberfläche uploaden. (Aber bitte komfortabel, also kein hex Editor *hrhr*)
-Dateien und Bilder in einer existierenden Datenbankstruktur abspeichern. Es ist eine mysql Datenbank und der Feldtyp ist longblob
-Es sollen Dateien bis mindestens um die 100MB speicherbar sein.
apollo13
User
Beiträge: 827
Registriert: Samstag 5. Februar 2005, 17:53

Eigenes Django ModelField implementieren und gut ist…
Lasse
User
Beiträge: 112
Registriert: Donnerstag 3. Februar 2011, 18:25

Danke an alle Anwortenden, das hat mir geholfen.

Ein eigenes ModelField habe ich jetzt (schon fast). Nur noch das abspeichern bereitet Probleme.
Hier die Funktion die für das Speichern zuständig ist:

Code: Alles auswählen

    def get_prep_value(self, value):
        return value.chunks().next()

    def get_db_prep_value(self, value, connection, prepared=False):
        if connection.settings_dict['ENGINE'] == 'django.db.backends.mysql':
            if not prepared:
                value = self.get_prep_value(value)
            return "X'" + binascii.hexlify(value).upper() + "'"
Funktioniert auch ohne Fehlermeldungen und es wird auch etwas in die Datenbank geschrieben, allerdings werden die hex buchstaben nicht in bytes umgewandelt.

Ich vermute dass Django Anführungszeichen um die Anweisung drumherum setzt. Stimmt das? Und wenn ja wie kann man dieses Verhalten ändern?
Zuletzt geändert von Lasse am Donnerstag 25. Oktober 2012, 08:30, insgesamt 2-mal geändert.
apollo13
User
Beiträge: 827
Registriert: Samstag 5. Februar 2005, 17:53

Siehe: https://docs.djangoproject.com/en/dev/h ... prep_value -- dabei vor allem: "This conversion should not include any database-specific conversions. If database-specific conversions are required, they should be made in the call to get_db_prep_value()."
Lasse
User
Beiträge: 112
Registriert: Donnerstag 3. Februar 2011, 18:25

@apollo13: Um Formsachen wollte ich mich eigendlich kümmern wenn es funktioniert. Trotzdem habe ich es jetzt so geändert, dass get_prep_value nur noch Datenbank unspezifische Aufgaben erfüllt. Villeicht kannst du mir ja jetzt eine Antwort auf meine Frage geben.
apollo13
User
Beiträge: 827
Registriert: Samstag 5. Februar 2005, 17:53

Ich habe zum Glück keine Ahnung von Mysql :) Aber wenn du Hilfe willst solltest du genauer werden: Wie schaut das komplette ModelField aus, was speicherst du, was ist dann in der DB, was ist der Query der ausgeführt wird (aus dem mysql query log) etc…
Lasse
User
Beiträge: 112
Registriert: Donnerstag 3. Februar 2011, 18:25

Eine Query die eigendlich eine Datei in das Blob Feld einfügen soll, allerdings fügt django zusätzliche Anführungszeichen vor und nach der Hex Anweisung ein -> Es wird exakt der hex code in die Datei geschrieben.

Code: Alles auswählen

		   37 Query	INSERT INTO `Document` (`ID`, `ArticleVariant`, `Description`, `Attachment`, `OriginalFileName`, `AttachmentSource`, `DocumentType`) VALUES ('fe00d0a5-4e2a-4f65-8b28-a7f407677e43', 'cb24cb9f-7db9-476a-853d-1cebea7341b1', '', 'X\'0F222228FFBBBB3737BB000073FBF827FF27FFFFFBB3B874000B300073BBFF728F27FFFF8BBB8F74470730003BBB8F82222788FFBBBB8F74B370F0001FF\'', '0.ico', '27528db8-63eb-47b4-98e1-859e69288ca9', '3e25a3ed-fc9b-4920-a856-9433ddb79f11')
Hier mal ein Beispiel wie so ein fehlerhaftes Blob dann aussieht wenn man es wieder aus der Datenbank holt:
X'0000010001002020040000000000E802000016000000280000002000000040000000010004000'
Es ist 1:1 das was ich darin abgelegt habe, der hex code von der Datei wurde nicht umgewandelt in binary.

Code: Alles auswählen

class DocumentField(Field):
    def __init__(self, *args, **kwargs):
        super(DocumentField, self).__init__(*args, **kwargs)
    
    def db_type(self, connection):
        #Muss fuer postgre Datenbank geaendert werden!
        return 'LONGBLOB'

    def formfield(self, **kwargs):
        defaults = {'form_class': forms.FileField}
        defaults.update(kwargs)
        return super(DocumentField, self).formfield(**defaults)

##    def get_prep_value(self, value):
##        return "X'" + binascii.hexlify(value.chunks().next()).upper() + "'"

    def get_prep_value(self, value):
        return value.chunks().next()

    def get_db_prep_value(self, value, connection, prepared=False):
        if connection.settings_dict['ENGINE'] == 'django.db.backends.mysql':
            if not prepared:
                value = self.get_prep_value(value)
            return "X'" + binascii.hexlify(value).upper() + "'"
apollo13
User
Beiträge: 827
Registriert: Samstag 5. Februar 2005, 17:53

Also einen formattierten String in get_db_prep_value zu returnen ist eher falsch, du musst das returnen was die MySQLdb API für ein Blob erwartet, was das ist steht sicherlich irgendwo in der MySQLdb Dokumentation.

EDIT:// Laut http://stackoverflow.com/questions/1294 ... ing-python sollte es reichen den Raw-String ohne hex encoding als zu speichern.
Antworten