Django. Substituir salvar para o modelo

134

Antes de salvar o modelo, redimensiono uma foto. Mas como posso verificar se uma nova imagem foi adicionada ou apenas uma descrição atualizada, para que eu possa pular o redimensionamento toda vez que o modelo for salvo?

class Model(model.Model):
    image=models.ImageField(upload_to='folder')
    thumb=models.ImageField(upload_to='folder')
    description=models.CharField()


    def save(self, *args, **kwargs):
        if self.image:
            small=rescale_image(self.image,width=100,height=100)
            self.image_small=SimpleUploadedFile(name,small_pic)
        super(Model, self).save(*args, **kwargs)

Desejo redimensionar novamente se a nova imagem for carregada ou a imagem atualizada, mas não quando a descrição for atualizada.

Pol
fonte
Você está redimensionando para um tamanho fixo de 100x100?
BDD
3
Você pode achar útil o django-imagekit útil
vikingosegundo

Respostas:

135

Alguns pensamentos:

class Model(model.Model):
    _image=models.ImageField(upload_to='folder')
    thumb=models.ImageField(upload_to='folder')
    description=models.CharField()

    def set_image(self, val):
        self._image = val
        self._image_changed = True

        # Or put whole logic in here
        small = rescale_image(self.image,width=100,height=100)
        self.image_small=SimpleUploadedFile(name,small_pic)

    def get_image(self):
        return self._image

    image = property(get_image, set_image)

    # this is not needed if small_image is created at set_image
    def save(self, *args, **kwargs):
        if getattr(self, '_image_changed', True):
            small=rescale_image(self.image,width=100,height=100)
            self.image_small=SimpleUploadedFile(name,small_pic)
        super(Model, self).save(*args, **kwargs)

Não tenho certeza se ele funcionaria bem com todas as ferramentas pseudo-auto django (Exemplo: ModelForm, contrib.admin etc).

petraszd
fonte
1
Parece legal. Mas não consigo renomear imagem para _image. Isso é importante?
Pol
Ok, eu resolvi com db_column = 'imagem'. Mas o aço não funciona!
Pol
1
É um método muito interessante .. por eu não entendo completamente. Você pode, por favor, explicar mais explicitamente? Ou semear algum artigo?
Pol
Também não funcionou para mim. set_image nunca ligou. Looks como este alguns Django material não suportado oficialmente
Ivan Borshchov
16

Verifique o campo pk do modelo. Se for Nenhum, então é um novo objeto.

class Model(model.Model):
    image=models.ImageField(upload_to='folder')
    thumb=models.ImageField(upload_to='folder')
    description=models.CharField()


    def save(self, *args, **kwargs):
        if 'form' in kwargs:
            form=kwargs['form']
        else:
            form=None

        if self.pk is None and form is not None and 'image' in form.changed_data:
            small=rescale_image(self.image,width=100,height=100)
            self.image_small=SimpleUploadedFile(name,small_pic)
        super(Model, self).save(*args, **kwargs)

Editar: eu adicionei uma verificação para 'imagem' em form.changed_data. Isso pressupõe que você esteja usando o site de administração para atualizar suas imagens. Você também precisará substituir o método save_model padrão, conforme indicado abaixo.

class ModelAdmin(admin.ModelAdmin):
    def save_model(self, request, obj, form, change):
        obj.save(form=form)
DM Graves
fonte
Acho que você está certo ... supondo que ele esteja usando o site de administração, ele pode substituir o save_model em seu AdminModel para passar o formulário para salvar e verificar se 'image' está em form.changed_data. Vou atualizar assim que tiver tempo.
DM Graves
Isso só funciona se o objeto for novo como você diz. Se você enviar uma nova imagem, o redimensionamento não será acionado.
Jonathan
2
"self.pk é None" não funciona se alguém especificar o ID, portanto: Model.objects.get_or_create (id = 234, ...) não funcionará nesta solução
nuts
6

Você pode fornecer um argumento extra para confirmar que uma nova imagem foi postada.
Algo como:

def save(self, new_image=False, *args, **kwargs):
    if new_image:
        small=rescale_image(self.image,width=100,height=100)
        self.image_small=SimpleUploadedFile(name,small_pic)
    super(Model, self).save(*args, **kwargs)

ou variável de solicitação de aprovação

def save(self, request=False, *args, **kwargs):
    if request and request.FILES.get('image',False):
        small=rescale_image(self.image,width=100,height=100)
        self.image_small=SimpleUploadedFile(name,small_pic)
    super(Model, self).save(*args, **kwargs)

Eu acho que isso não vai prejudicar sua defesa quando chamado simplesmente.

Você pode colocar isso em seu admin.py para que isso funcione também com o site de administração (na segunda das soluções acima):

class ModelAdmin(admin.ModelAdmin):

    ....
    def save_model(self, request, obj, form, change): 
        instance = form.save(commit=False)
        instance.save(request=request)
        return instance
crodjer
fonte
ele me dizer que: objeto 'WSGIRequest' tem nenhum atributo 'FILE'
Pol
SRY seus arquivos em vez de arquivos, atualizados para request.FILES.get ( 'imagem', False) em vez de request.FILES [ 'image'], isso vai evitar a exceção
crodjer
3

O que eu fiz para alcançar o objetivo foi fazer isso ..

# I added an extra_command argument that defaults to blank
def save(self, extra_command="", *args, **kwargs):

e abaixo do método save () é este ..

# override the save method to create an image thumbnail
if self.image and extra_command != "skip creating photo thumbnail":
    # your logic here

então quando eu edito alguns campos mas não editando a imagem, eu coloco isso ..

Model.save("skip creating photo thumbnail")

você pode substituir o "skip creating photo thumbnail"com "im just editing the description"ou um texto mais formal.

Espero que este ajude!

bonbon.langes
fonte
2

Consulte o banco de dados para um registro existente com a mesma PK. Compare os tamanhos de arquivo e as somas de verificação das imagens novas e existentes para ver se são iguais.

Ignacio Vazquez-Abrams
fonte
1

Django 3: Substituindo métodos de modelo predefinidos

from django.db import models

class Blog(models.Model):
    name = models.CharField(max_length=100)
    tagline = models.TextField()

    def save(self, *args, **kwargs):
        do_something()
        super().save(*args, **kwargs)  # Call the "real" save() method.
        do_something_else()

É importante lembrar de chamar o método da superclasse - esse é o super().save(*args, **kwargs)negócio - para garantir que o objeto ainda seja salvo no banco de dados. Se você esquecer de chamar o método da superclasse, o comportamento padrão não ocorrerá e o banco de dados não será afetado.

panchicore
fonte
0

Na nova versão, é assim:

def validate(self, attrs):
    has_unknown_fields = set(self.initial_data) - set(self.fields.keys())
    if has_unknown_fields:
        raise serializers.ValidationError("Do not send extra fields")
    return attrs
Dan Goriaynov
fonte
0

Encontrei uma outra maneira simples de armazenar os dados no banco de dados

models.py

class LinkModel(models.Model):
    link = models.CharField(max_length=500)
    shortLink = models.CharField(max_length=30,unique=True)

No banco de dados eu tenho apenas 2 variáveis

views.py

class HomeView(TemplateView):
    def post(self,request, *args, **kwargs):
        form = LinkForm(request.POST)

        if form.is_valid():
            text = form.cleaned_data['link'] # text for link

        dbobj = LinkModel()
        dbobj.link = text
        self.no = self.gen.generateShortLink() # no for shortLink
        dbobj.shortLink = str(self.no)
        dbobj.save()         # Saving from views.py

Neste, criei a instância do modelo em views.py apenas e colocando / salvando dados em 2 variáveis ​​apenas de visualizações.

Devendra Bhat
fonte