Django, want to upload either image (ImageField) or file (FileField) - django

I have a form in my html page, that prompts user to upload File or Image to the server. I want to be able to upload ether file or image. Let's say if user choose file, image should be null, and vice verso. Right now I can only upload both of them, without error. But If I choose to upload only one of them (let's say I choose image) I will get an error:
"Key 'attachment' not found in <MultiValueDict: {u'image': [<InMemoryUploadedFile: police.jpg (image/jpeg)>]}>"
models.py:
#Description of the file
class FileDescription(models.Model):
TYPE_CHOICES = (
('homework', 'Homework'),
('class', 'Class Papers'),
('random', 'Random Papers')
)
subject = models.ForeignKey('Subjects', null=True, blank=True)
subject_name = models.CharField(max_length=100, unique=False)
category = models.CharField(max_length=100, unique=False, blank=True, null=True)
file_type = models.CharField(max_length=100, choices=TYPE_CHOICES, unique=False)
file_uploaded_by = models.CharField(max_length=100, unique=False)
file_name = models.CharField(max_length=100, unique=False)
file_description = models.TextField(unique=False, blank=True, null=True)
file_creation_time = models.DateTimeField(editable=False)
file_modified_time = models.DateTimeField()
attachment = models.FileField(upload_to='files', blank=True, null=True, max_length=255)
image = models.ImageField(upload_to='files', blank=True, null=True, max_length=255)
def __unicode__(self):
return u'%s' % (self.file_name)
def get_fields(self):
return [(field, field.value_to_string(self)) for field in FileDescription._meta.fields]
def filename(self):
return os.path.basename(self.image.name)
def category_update(self):
category = self.file_name
return category
def save(self, *args, **kwargs):
if self.category is None:
self.category = FileDescription.category_update(self)
for field in self._meta.fields:
if field.name == 'image' or field.name == 'attachment':
field.upload_to = 'files/%s/%s/' % (self.file_uploaded_by, self.file_type)
if not self.id:
self.file_creation_time = datetime.now()
self.file_modified_time = datetime.now()
super(FileDescription, self).save(*args, **kwargs)
forms.py
class ContentForm(forms.ModelForm):
file_name =forms.CharField(max_length=255, widget=forms.TextInput(attrs={'size':20}))
file_description = forms.CharField(widget=forms.Textarea(attrs={'rows':4, 'cols':25}))
class Meta:
model = FileDescription
exclude = ('subject',
'subject_name',
'file_uploaded_by',
'file_creation_time',
'file_modified_time',
'vote')
def clean_file_name(self):
name = self.cleaned_data['file_name']
# check the length of the file name
if len(name) < 2:
raise forms.ValidationError('File name is too short')
# check if file with same name is already exists
if FileDescription.objects.filter(file_name = name).exists():
raise forms.ValidationError('File with this name already exists')
else:
return name
views.py
if request.method == "POST":
if "upload-b" in request.POST:
form = ContentForm(request.POST, request.FILES, instance=subject_id)
if form.is_valid(): # need to add some clean functions
# handle_uploaded_file(request.FILES['attachment'],
# request.user.username,
# request.POST['file_type'])
form.save()
up_f = FileDescription.objects.get_or_create(
subject=subject_id,
subject_name=subject_name,
category = request.POST['category'],
file_type=request.POST['file_type'],
file_uploaded_by = username,
file_name=form.cleaned_data['file_name'],
file_description=request.POST['file_description'],
image = request.FILES['image'],
attachment = request.FILES['attachment'],
)
return HttpResponseRedirect(".")

Let's say if user choose file, image should be null, and vice verso.
You could:
make an SQL constraint,
override model.save() to fail if either file or image is blank,
define ContentForm.clean() to raise a ValidationError if either file or image is blank, see Cleaning and validating fields that depend on each other.
Also be careful that up_f will be a tuple in:
up_f = FileDescription.objects.get_or_create(

I've had the same problem. For some reason the key value dict only takes one key pair values. save the the attachment like so
attachment=form.data['attachment']
as opposed to
attachment=request.FILES['attachment']
it should run, but curious if it will save as a file.
i know this question is old but had a difficult time with this same issue

Create radio buttons for the user to chose what he/she want to upload and use only one FileField attribute in the model. You can convert the other field to BooleanField or CharField to indicate what the user selected.

Related

Django Many to Many Field Set in Model Save Method

I am trying to override the save method in a model with logic to update a couple of many to many fields. Using print statements I can see values updating as expected but the values are not persisted after save.
In the below model the change_access_flag is changing as expected with a signal, the prints are executing with the appropriate values, but the allowed_departments and allowed_communities fields are not updating with the printed values.
Model
class Person(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
full_name = models.CharField(null=True, blank=True, max_length=50)
payroll_id = models.CharField(null=True, max_length=20)
position = models.ForeignKey(Position, null=True, on_delete=models.SET_NULL)
primary_community = models.ForeignKey(Community, null=True, on_delete=models.CASCADE, related_name="primary_community")
region = models.CharField(max_length=2, choices=RegionChoices.choices, blank=True, null=True)
allowed_communities = models.ManyToManyField(Community, blank=True, related_name="allowed_community")
allowed_departments = models.ManyToManyField(Department, blank=True)
access_change_flag = models.BooleanField(default=False)
def __str__(self):
return f'{self.user.first_name} {self.user.last_name}'
class Meta:
verbose_name_plural = "People"
ordering = ['position__position_code', 'user__last_name', 'full_name']
def save(self, *args, **kwargs):
#Set Full Name field
if self.user.last_name:
self.full_name = f'{self.user.first_name} {self.user.last_name}'
super().save(*args, **kwargs)
#Change flag set in signals, set for events that require updating access settings
if self.access_change_flag:
self.access_change_flag = False
#Allowed community access
access_level = self.position.location_access_level
self.allowed_communities.clear()
if access_level == 'R':
if self.primary_community.community_name == '#':
region = self.region
else:
region = self.primary_community.region
if region is not None:
communities = Community.objects.filter(region=region)
self.allowed_communities.set(communities)
self.allowed_communities.add(self.primary_community)
elif access_level == 'A':
communities = Community.objects.filter(active=True)
self.allowed_communities.set(communities)
else:
communities = self.primary_community
self.allowed_communities.add(communities)
print(self.allowed_communities.all())
#Allowed department access
dept_access = self.position.department_only_access
if dept_access:
depts = [self.position.department]
else:
depts = Department.objects.filter(active=True)
self.allowed_departments.set(depts)
print(self.allowed_departments.all())
super().save(*args, **kwargs)
I have tried variations of set, clear, add, moving the super.save() around, and placing the logic in a signal but nothing seems to work. I have tested initiating save from both a model form through a view and admin.
Let me answer in quotes. You can find the source in this section.
If you wish to update a field value in the save() method, you may also
want to have this field added to the update_fields keyword argument.
This will ensure the field is saved when update_fields is specified.
Also read here
Specifying update_fields will force an update.
So try to call the super().save(*args, **kwargs) method at the end with defining the argument update_fields. This will force the update of your model regarding the specified fields.
Let me know how it goes.

Django Form If condition in view.py with 2 instance

TO SAVE DATA that is inputted in form in Django i tried tomake it like this
I put this in my model.py
class Item(models.Model):
CATEGORY = (
('Gudang Kering', 'Gudang Kering'),
('Gudang Basah','Gudang Basah'),
)
name = models.CharField(max_length=200,null= True)
stock = models.IntegerField(default='0', blank=False, null=True)
category = models.CharField(max_length=200,null= True,choices=CATEGORY)
reorderlevel = models.IntegerField(default='0', blank=False, null=True)
maxreorderlevel = models.IntegerField(default='0', blank=False, null=True)
description = models.CharField(max_length=200,null= True, blank= True)
date_created = models.DateTimeField(auto_now_add= True)
tags = models.ManyToManyField(Tag)
def __str__(self):
return self.name
class Issue(models.Model):
STATUS = (
('Pending', 'Pending'),
('Granted','Granted'),
('Denied','Denied'),
)
customer = models.ForeignKey(Customer, null=True, on_delete= models.SET_NULL)
item = models.ForeignKey(Item, null=True, on_delete= models.SET_NULL)
quantity = models.IntegerField(default='0', blank=False, null=True)
date_created = models.DateTimeField(auto_now_add=True, auto_now=False)
status = models.CharField(max_length=200,null= True, choices=STATUS)
Then in view.py i define the form like this
def updateIssue(request, pk):
issue = Issue.objects.get(id=pk)
item = Item.objects.all()
form = UpdateIssueForm(instance=issue)
if request.method == 'POST':
form = UpdateIssueForm(request.POST,instance=issue)
#print ('printing:',request.POST)
if form.is_valid():
instance = form.save(commit=False)
if instance.status == 'Granted':
item.stock -= instance.quantity
instance.save()
item.save()
else:
instance.save()
return redirect('/')
context = {'form':form}
return render(request,'accounts/issue_form.html',context)``
The Goal
if instance == "Granted"
the item.stock will be decreased on the amount of instance.quantity
and will be saved.
else
instance will just be saved without affecting the stock from the 2nd model
The error
item = Item.objects.all()
even when called the item.stock have 0 attribute even when i have input data in database for that table
There is no need to get Item since we can access the Item related to Issue using the Issue object like issue.item. This is just an example from docs:
Article model has a field reporter which is a ForeignKey realted to Reporter model. Using Article object the Reporter object is accessed.
>>> new_article = r.article_set.create(headline="John's second story", pub_date=date(2005, 7, 29))
>>> new_article
<Article: John's second story>
>>> new_article.reporter
<Reporter: John Smith>
>>> new_article.reporter.id
1
Like so, we can access the Item using Issue
def updateIssue(request, pk):
issue = Issue.objects.get(id=pk) # we have our Issue here
form = UpdateIssueForm(instance=issue)
if request.method == 'POST':
form = UpdateIssueForm(request.POST,instance=issue)
if form.is_valid():
instance = form.save(commit=False)
if instance.status == 'Granted':
issue.item.stock -= instance.quantity # access Item by using Issue object's related field with name item
issue.item.save() # save the Item first
instance.save() # then the Issue
else:
instance.save()
return redirect('/')
context = {'form':form}
return render(request,'accounts/issue_form.html',context)

Django - Allow only one filed to fill in the form

I am using below django model for forms
class Post(models.Model):
author = models.ForeignKey(CustomUser,on_delete=models.CASCADE,)
text = models.TextField()
created_date = models.DateTimeField(default=timezone.now)
post_url = models.URLField(max_length = 200, blank = True)
picture = models.ImageField(upload_to='Image_folder', height_field=None, width_field=None, max_length=100, blank = True)
slug = models.SlugField(unique=True, blank=True)
I want to allow user to enter only one field either post_url or picture but not both. Can some give some an idea how to implement this?
# you can use clean or clean_yourfieldname function in your forms.py class
For Example
def clean(self):
cleaned_data = super().clean()
post_url = cleaned_data.get("post_url")
picture = cleaned_data.get("picture")
if post_url and picture:
raise forms.ValidationError(
"Please fill either post_url or picture field, but not both"
)
return cleaned_data

Making a custom id field in Django models

I'm trying to make a model in Django that has a custom id attribute. I want it to always equal the sum of 10000 and the current id number of that instance. How exactly do I write that? And do I have to do anything in the view?
Edit: I better put the code I'm using just in case.
models.py
class Schedules(models.Model):
course_name = models.CharField(max_length=128, choices=COURSE_NAME_CHOICES, default='a-plus')
location = models.CharField(max_length=128, choices=LOCATION_CHOICES, default='south_plainfield')
room = models.CharField(max_length=128, choices=ROOM_CHOICES, default='A')
start_date = models.DateField(auto_now=False, auto_now_add=False, default=datetime.date.today)
start_time = models.CharField(max_length=128, choices=START_TIME_CHOICES, default='eight-thirty am')
end_time = models.CharField(max_length=128, choices=END_TIME_CHOICES, default='eight-thirty am')
instructor = models.CharField(max_length=128, choices=INSTRUCTOR_CHOICES, default='adewale')
total_hours = models.CharField(max_length=128, choices=TOTAL_HOURS_CHOICES, default='six')
hours_per_class = models.CharField(max_length=128, choices=HOURS_PER_CLASS_CHOICES, default='four_and_half')
frequency = models.CharField(max_length=128)
status = models.CharField(max_length=128, choices=STATUS_CHOICES)
interval = models.CharField(max_length=128, choices=INTERVAL_CHOICES, default='1 day')
initiated_by = models.CharField(max_length=128, null=True)
schedule_id = models.IntegerField(default=0)
views.py
def start_One_Schedule(request):
form = ScheduleForm()
if request.method == 'POST':
form = ScheduleForm(request.POST)
if form.is_valid():
obj = form.save(commit=False)
obj.initiated_by = request.user.username
obj.save()
return render(request, 'schedule/schedule.html', {})
else:
print(form.errors)
return render(request, 'schedule/start_one_schedule.html', {'form': form})
Create a property on the model that adds 10000 to the id field. You don't need to create the actual field.
class Example(models.Model):
#property
def custom_id(self):
return self.id + 10000
You can write custom id field in you model like this
class ABC(models.Model):
custom_id = models.IntegerField(default=0)
def save(self, flag=True, *args, **kwargs):
# Save your object. After this line, value of custom_id will be 0 which is default value
super(ABC, self).save(flag=True, *args, **kwargs)
# Here value of custom_id will be updated according to your id value
if flag:
self.custom_id = self.id + 10000
self.save(flag=False, *args, **kwargs)
flag is required as otherwise it will start recursion which will run infinitely.

How can I find out why a Django database .save() failed?

My Django database .save() is throwing an exception. Is there a way to find out why?
My main code is:
for i in data['movies']:
try:
id = i['regions'][0]['products'][0]['product_id']
title = i['regions'][0]['products'][0]['product_title']
m = Movie.objects.get(preview_id=id)
except Movie.DoesNotExist:
try:
movie = Movie()
movie.preview_id = id
movie.title = title
movie.details = i['regions'][0]['products'][0]['description']
movie.date = i['regions'][0]['products'][0]['upd_date']
movie.trailer = i['regions'][0]['products'][0]['clips'][0]['files'][0]['url']
movie.likes = 0
movie.dislikes = 0
movie.save() # THIS IS THROWING AN ERROR
print id + ": " + title
for genre in i['regions'][0]['categories']:
try:
g = Genre.objects.get(title__exact=genre['name'])
movie.genres.add(g)
except Genre.DoesNotExist:
g = Genre(title=genre['name'])
g.save()
movie.genres.add(g)
for pic in i['regions'][0]['pictures']:
if pic['type_name'] == "poster_large":
movie.picture = pic['url']
movie.save()
except:
print 'error'
print 'Success'
My movies model looks like this:
class Movie(models.Model):
# Movie Model store all the detail of a movie
# Each movie is created by a User
user = models.ForeignKey(User)
title = models.CharField(max_length=200)
slug = models.SlugField(max_length=200, blank=True, null=True)
details = models.TextField()
picture = models.ImageField(upload_to=filename, blank=True, null=True)
trailer = models.FileField(upload_to=videoname, blank=True, null=True)
# genres = ManyToMany with Genre Model
genres = models.ManyToManyField(Genre, related_name='genres')
likes = models.BigIntegerField(blank=True, null=True)
dislikes = models.BigIntegerField(blank=True, null=True)
trigahs = models.BigIntegerField(blank=True, null=True)
# Director with People Model
director = models.ForeignKey(People, blank=True, null=True)
# Casts = ManyToMany with People Model
casts = models.ManyToManyField(People, related_name='casts', blank=True, null=True)
date = models.DateField(blank=True, null=True)
amazon_id = models.CharField(max_length=200, blank=True, null=True)
preview_id = models.BigIntegerField(blank=True, null=True)
created_on = models.DateTimeField(auto_now_add=True)
def add_likes(self):
self.likes = self.likes + 1
self.save()
def add_dislikes(self):
self.dislikes = self.dislikes + 1
self.save()
def save(self):
super(Movie, self).save()
if not self.slug:
self.slug = '%s' % (
slugify(self.title)
)
super(Movie, self).save()
And the super function is defined as follows:
def save(self, force_insert=False, force_update=False, using=None,
update_fields=None):
"""
Saves the current instance. Override this in a subclass if you want to
control the saving process.
The 'force_insert' and 'force_update' parameters can be used to insist
that the "save" must be an SQL insert or update (or equivalent for
non-SQL backends), respectively. Normally, they should not be set.
"""
# Ensure that a model instance without a PK hasn't been assigned to
# a ForeignKey or OneToOneField on this model. If the field is
# nullable, allowing the save() would result in silent data loss.
for field in self._meta.concrete_fields:
if field.is_relation:
# If the related field isn't cached, then an instance hasn't
# been assigned and there's no need to worry about this check.
try:
getattr(self, field.get_cache_name())
except AttributeError:
continue
obj = getattr(self, field.name, None)
# A pk may have been assigned manually to a model instance not
# saved to the database (or auto-generated in a case like
# UUIDField), but we allow the save to proceed and rely on the
# database to raise an IntegrityError if applicable. If
# constraints aren't supported by the database, there's the
# unavoidable risk of data corruption.
if obj and obj.pk is None:
raise ValueError(
"save() prohibited to prevent data loss due to "
"unsaved related object '%s'." % field.name
)
using = using or router.db_for_write(self.__class__, instance=self)
if force_insert and (force_update or update_fields):
raise ValueError("Cannot force both insert and updating in model saving.")
if update_fields is not None:
# If update_fields is empty, skip the save. We do also check for
# no-op saves later on for inheritance cases. This bailout is
# still needed for skipping signal sending.
if len(update_fields) == 0:
return
update_fields = frozenset(update_fields)
field_names = set()
for field in self._meta.fields:
if not field.primary_key:
field_names.add(field.name)
if field.name != field.attname:
field_names.add(field.attname)
non_model_fields = update_fields.difference(field_names)
if non_model_fields:
raise ValueError("The following fields do not exist in this "
"model or are m2m fields: %s"
% ', '.join(non_model_fields))
# If saving to the same database, and this model is deferred, then
# automatically do a "update_fields" save on the loaded fields.
elif not force_insert and self._deferred and using == self._state.db:
field_names = set()
for field in self._meta.concrete_fields:
if not field.primary_key and not hasattr(field, 'through'):
field_names.add(field.attname)
deferred_fields = [
f.attname for f in self._meta.fields
if (f.attname not in self.__dict__ and
isinstance(self.__class__.__dict__[f.attname], DeferredAttribute))
]
loaded_fields = field_names.difference(deferred_fields)
if loaded_fields:
update_fields = frozenset(loaded_fields)
self.save_base(using=using, force_insert=force_insert,
force_update=force_update, update_fields=update_fields)
save.alters_data = True
This is all code I am taking over so much of it is a mystery to me. Apologies if this question is not properly framed. But could really do with a pointer as to how to track down the reason for failure.
try:
line 1
line 2
except Exception as e:
print e
This will reveal the error. This is only for debugging. You should properly handle the exceptions.