Django:添加一个允许多张图片的字段。

huangapple go评论58阅读模式
英文:

Django: Adding a field which allows multiple images

问题

Forms.py

class SellerForm(forms.ModelForm):
    username = forms.CharField()

    class Meta:
        model = Seller
        fields = [
            'username',
            'first_name',
            'last_name',
            'country',
            'image_of_seller',
            'city',
            'date_of_birth',
            'describe_yourself'
        ]

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.fields['username'].initial = self.instance.seller.username
    
    @transaction.atomic
    def save(self, commit=True):
        seller = super().save(commit=False)
        user = seller.seller
        self.instance.seller.username = self.cleaned_data['username']
    
        if commit:
            seller.save()
            user.save()
    
        return seller

Views.py

class SellerUpdateView(UpdateView):
    model = Seller
    form_class = SellerForm
    template_name = 'core/seller_update_form.html'

    def get_object(self, queryset=None):
        return self.request.user.seller

Models.py

class Seller(models.Model):
    seller = models.OneToOneField(User, on_delete=models.CASCADE)
    first_name = models.CharField(max_length=50, null=False, blank=False)
    last_name = models.CharField(max_length=50, default=False)
    country = models.CharField(max_length=50, null=False, blank=False)
    identification = models.ImageField(null=False, blank=False)
    image_of_seller =  models.ImageField(null=False, blank=True)
    city = models.CharField(max_length=30)
    date_of_birth = models.DateField(null=False, blank=False)
    describe_yourself = models.TextField(null=False, blank=False)
    slug = models.SlugField(unique=True, blank=True)

    def __str__(self):
        return self.seller.username
    
    def save(self, *args, **kwargs):
        self.slug = slugify(self.seller.username)
        super().save(*args, **kwargs)
英文:

Forms.py

class SellerForm(forms.ModelForm):
username = forms.CharField()
class Meta:
model = Seller
fields = [
'username',
'first_name',
'last_name',
'country',
'image_of_seller',
'city',
'date_of_birth',
'describe_yourself'
]

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.fields['username'].initial = self.instance.seller.username
    
    @transaction.atomic
    def save(self, commit=True):
        seller = super().save(commit=False)
        user = seller.seller
        self.instance.seller.username = self.cleaned_data['username']
    
        if commit:
            seller.save()
            user.save()
    
        return seller

Views.py


class SellerUpdateView(UpdateView):
model = Seller
form_class = SellerForm
template_name = 'core/seller_update_form.html'

    def get_object(self, queryset=None):
        return self.request.user.seller

Models.py

class Seller(models.Model):
seller = models.OneToOneField(User, on_delete=models.CASCADE)
first_name = models.CharField(max_length=50, null=False, blank=False)
last_name = models.CharField(max_length=50, default=False)
country = models.CharField(max_length=50, null=False, blank=False)
identification = models.ImageField(null=False, blank=False)
image_of_seller =  models.ImageField(null=False, blank=True) # I want this to accept multiple files, which are images.
city = models.CharField(max_length=30)
date_of_birth = models.DateField(null=False, blank=False)
describe_yourself = models.TextField(null=False, blank=False)
slug = models.SlugField(unique = True, blank = True)

    def __str__(self):
        return self.seller.username
    
    def save(self, *args, **kwargs):
        self.slug = slugify(self.seller.username)
        super().save(*args, **kwargs)

I know I can create another model, with a ForeignKey to the Seller model, then have inline while creating ModelAdmin. But is there a way to have this for the form instead? I want the user to be able to update their pictures.

Update

I am thinking of something that behaves like StackedInline, like the example below. But instead rendering it not in admin, but instead in the SellerForm.

class Seller(models.Model):
    #changed from seller to user, just for readability. 
    user = models.OneToOneField(User, on_delete=models.CASCADE)
    first_name = models.CharField(max_length=50, null=False, blank=False)
    last_name = models.CharField(max_length=50, default=False)
    country = models.CharField(max_length=50, null=False, blank=False)
    identification = models.ImageField(null=False, blank=False)
    city = models.CharField(max_length=30)
    date_of_birth = models.DateField(null=False, blank=False)
    describe_yourself = models.TextField(null=False, blank=False)
    slug = models.SlugField(unique = True, blank = True)
    age = models.IntegerField(default=0)

    @property
    def calculate_age(self):
        today = datetime.date.today()
        year = today.strftime("%Y")
        current_year = int(year)
        user_year = int(self.date_of_birth.year)
        age = current_year - user_year
        self.age = age 
        return age
    
    def __str__(self):
        return self.seller.username
    
    def save(self, *args, **kwargs):
        self.calculate_age
        self.slug = slugify(self.seller.username)
        super().save(*args, **kwargs)


class ImageOfSeller(models.Model):
    seller = models.ForeignKey(Seller, on_delete=models.CASCADE)
    Image = models.ImageField()

# Admin.py 


class ImageSellerInline(admin.StackedInline):
    model = ImageOfSeller 
    can_delete = True 
    verbose_name_plural = 'seller_image'
    extra = 0

class SellerAdmin(admin.ModelAdmin):
    inlines = [
        ImageSellerInline,
    ]
admin.site.register(Seller, SellerAdmin)

答案1

得分: 0

基于您的评论和我理解的内容,您可以通过将您的模型中的图像字段设置为ArrayField来实现这一点:

class Seller(models.Model):
    images = models.ArrayField(default=list)

然后,您可以在表单中这样使用它:

class SellerForm(forms.ModelForm):
    images = forms.FileField(widget=forms.ClearableFileInput(attrs={'multiple': True}), required=False)

但您需要在视图中手动处理数据:

form = SellerForm(request.POST, request.FILES, instance=seller)
if form.is_valid():
    seller = form.save(commit=False)
    images = request.FILES.getlist('images')
    for image in images:
        seller.images.append(image)
    seller.save()

但是,我不建议使用这种方法。更好的方法是创建另一个具有ImageField和FK到您的主模型的模型。然后,您可以使用Django的表单集来处理多个实例和内联,您可以在主文档中找到许多示例:

https://docs.djangoproject.com/en/4.2/topics/forms/formsets/

英文:

Based on your comment and what I understood, you can achieve this by having your image field as ArrayField in your model:

class Seller(models.Model):
    images = models.ArrayField(default=list)

then you can make it work in your form like this:

class SellerForm(forms.ModelForm):
    images = forms.FileField(widget=forms.ClearableFileInput(attrs={'multiple': True}), required=False)

but you have to handle the data manually in your view:

form = SellerForm(request.POST, request.FILES, instance=seller)
        if form.is_valid():
            seller = form.save(commit=False)
            images = request.FILES.getlist('images')
            for image in images:
                seller.images.append(image)
            seller.save()

BUT I would not recommend this approach. The better way is to create another model with ImageField and FK to your main model. then you can use django formsets to handle multiple instances and inlines which you can find many examples, this is from the main doc:

https://docs.djangoproject.com/en/4.2/topics/forms/formsets/

答案2

得分: 0

我找到了另一种解决方案。

首先,我创建了自己的字段,允许我上传多个文件。 MultipleFileField

from django.forms import ClearableFileInput, FileField 

class MultipleFileInput(ClearableFileInput):
    allow_multiple_selected = True

class MultipleFileField(FileField):
    def __init__(self, *args, **kwargs):
        kwargs.setdefault("widget", MultipleFileInput())
        super().__init__(*args, **kwargs)

    def clean(self, data, initial=None):
        single_file_clean = super().clean
        if isinstance(data, (list, tuple)):
            result = [single_file_clean(d, initial) for d in data]
        else:
            result = single_file_clean(data, initial)
        return result

然后,我添加了 validate_image_file_extension,用于验证图像是否是真实图像。 validate_image_file_extension,我使用了 default_validators 属性。 default_validators

from django.forms import ClearableFileInput, FileField 
from django.core.validators import validate_image_file_extension

class MultipleFileInput(ClearableFileInput):
    allow_multiple_selected = True

class MultipleFileField(FileField):
    default_validators = [validate_image_file_extension]
    def __init__(self, *args, **kwargs):
        kwargs.setdefault("widget", MultipleFileInput())
        super().__init__(*args, **kwargs)

    def clean(self, data, initial=None):
        single_file_clean = super().clean
        if isinstance(data, (list, tuple)):
            result = [single_file_clean(d, initial) for d in data]
        else:
            result = single_file_clean(data, initial)
        return result

然后,我在我的表单中使用了我的字段 MultipleFileField,类似于这样:image_of_seller = MultipleFileField()

接着,我在我的表单中使用了 cleaned_data 来获取 MultipleFileField,类似于这样:

def clean(self):
    # 其他内容...
    self.image_of_seller = cleaned_data.get('image_of_seller')

然后,我使用另一个模型,该模型具有对我的用户模型的外键,并基于文件创建对象。

示例

for file in images:
    ImageSeller.objects.create(
          seller=seller_user,
          image=file,
    )
英文:

I found a another solution everyone.

First I created my own field instead, which allows me to upload multiple files. MultipleFileField.

from django.forms import ClearableFileInput, FileField 

class MultipleFileInput(ClearableFileInput):
    allow_multiple_selected = True

class MultipleFileField(FileField):
    def __init__(self, *args, **kwargs):
        kwargs.setdefault("widget", MultipleFileInput())
        super().__init__(*args, **kwargs)

    def clean(self, data, initial=None):
        single_file_clean = super().clean
        if isinstance(data, (list, tuple)):
            result = [single_file_clean(d, initial) for d in data]
        else:
            result = single_file_clean(data, initial)
        return result

Then I added validate_image_file_extension which validates if the image is an actual image. validate_image_file_extension, where I use the default_validators attribute. default_validators.

from django.forms import ClearableFileInput, FileField 
from django.core.validators import validate_image_file_extension

class MultipleFileInput(ClearableFileInput):
    allow_multiple_selected = True

class MultipleFileField(FileField):
    default_validators = [validate_image_file_extension]
    def __init__(self, *args, **kwargs):
        kwargs.setdefault("widget", MultipleFileInput())
        super().__init__(*args, **kwargs)

    def clean(self, data, initial=None):
        single_file_clean = super().clean
        if isinstance(data, (list, tuple)):
            result = [single_file_clean(d, initial) for d in data]
        else:
            result = single_file_clean(data, initial)
        return result

Then I use my field, MultipleFileField for my form, something like this image_of_seller = MultipleFileField()

I then use cleaned_data on MultipleFileField in my form, something like this,

def clean(self):
    # other stuff ...
    self.image_of_seller = cleaned_data_get('image_of_seller')

Then I use my another model, which has ForeignKey to my User model, and create objects based on the files.

Example

for file in images:
    ImageSeller.objects.create(
          seller=seller_user,
          image=file,
    )

huangapple
  • 本文由 发表于 2023年5月21日 17:14:44
  • 转载请务必保留本文链接:https://go.coder-hub.com/76299130.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定