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 it 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.
I am thinking of something that behaves like StackedInline
, like the example below, but instead rendering it not in admin, but 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)
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, using the default_validators
attribute.
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 = self.cleaned_data.get('image_of_seller')
Then, I use another model, which has a ForeignKey
to my User model, and create objects based on the files.
for file in images:
ImageSeller.objects.create(
seller=seller_user,
image=file,
)
Use bulk_create
for the example above