英文:
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,
)
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论