I have a simple form which is used for submitting files which worked fine. I've since moved the file out from a field into its own table, now I'm really struggling as I need to add the file to the extra table and save the correct foreignkey in the form table. I've tried various snippets in save(), grabbing the file in the view and adding the file as an extra field but nothing is quite working.
The fairly obvious error is:
"Cannot assign InMemoryUploadedFile: Resource.file must be a File
instance"
class File(models.Model):
filename = models.CharField('Filename', max_length=200)
file = models.FileField()
mimetype = models.CharField('Mimetype', max_length=200)
pub_date = models.DateTimeField('date published', auto_now_add=True, blank=True)
def __str__(self):
return self.filename
class Resource(models.Model):
list_display = ('title', 'approved')
def __str__(self):
return str(self.title)
title = models.CharField('Title', max_length=200)
description = models.TextField('Description', null=True)
file = models.ForeignKey(File)
... cut for brevity
class ResourceForm(forms.ModelForm):
class Meta:
model = Resource
the problem is that you assign a file into a foreign key field. You need to instantiate the File object first and then set in to Resource object.
file = File(filename="test", file=temp_data, mimetype='pdf')
file.save()
resource.file = file
resource.save()
Related
When I delete the parent table using Django rest due to on_delete it deletes the child table also (due to foreign key) but the Image and File content of the child table not deleted.
I want to delete those images and Files also..!
How to do that..?
My tables
1:Group
2:Posts
One To Many Relation
I delete the Group table as follows:
GroupsModel.objects.filter(id=py_data.get('group_id')).delete()
GroupsModel :
class GroupsModel(models.Model):
group_name = models.CharField(max_length=20, unique=True)
group_description = models.CharField(max_length=50)
group_joining_link = models.CharField(max_length=50, default='', unique=True)
user_id = models.ManyToManyField(UserModel, through='UserModelGroupsModel', related_name='u_g')
class Meta:
db_table = 'groups'
GroupPostsModel:
class GroupPostsModel(models.Model):
post_text = models.CharField(max_length=1000)
post_type = models.CharField(max_length=20)
image = models.ImageField(blank=True, null=True, upload_to='post_images/')
document = models.FileField(blank=True,null=True, upload_to='post_documents/')
likes = models.IntegerField(default=0)
time_stamp = models.DateTimeField(auto_now=True)
group = models.ForeignKey(GroupsModel, on_delete=models.CASCADE)
user = models.ForeignKey(UserModel, on_delete=models.CASCADE)
class Meta:
db_table = 'group_posts'
I want to delete image and document file also automatically.
When an instance is removed referred files are not deleted, since the ImageField/FileField is only a reference to the files. You can override the delete method of your model:
import os
class GroupPostsModel(models.Model):
post_text = models.CharField(max_length=1000)
post_type = models.CharField(max_length=20)
image = models.ImageField(blank=True, null=True, upload_to='post_images/')
document = models.FileField(blank=True,null=True, upload_to='post_documents/')
likes = models.IntegerField(default=0)
time_stamp = models.DateTimeField(auto_now=True)
group = models.ForeignKey(GroupsModel, on_delete=models.CASCADE)
user = models.ForeignKey(UserModel, on_delete=models.CASCADE)
def delete(self):
if self.image:
if os.path.isfile(self.image.path):
os.remove(self.image.path)
if self.document:
if os.path.isfile(self.document.path):
os.remove(self.document.path)
super().delete()
class Meta:
db_table = 'group_posts'
The delete method will not be called in the queryset you wrote, you need to delete each instance individually:
for instance in GroupsModel.objects.filter(id=py_data.get('group_id')):
instance.delete()
on_delete=models.CASCADE on a ForeignKey is something written and managed on the database schema. It is not Django that delete the child model on delete, but your database (MySQL, Postgres, ...) that automaticaly detect a failure on ForeignKey and perform a Cascade action (like a contraint).
models.ImageField and models.FileField are (at database level) just a CharField with the location on the file system of your file. The database cannot remove a file, so you cannot delegate this action to the database.
If you want automatic removal of file on Model .delete(), you have multiple possible strategy :
Periodic check that for each file, a model exists (if not, remove the file)
Overwrite the .delete() method of GroupPostsModel (but it will only work if you call .delete() on an instance of the model, not on a queryset, like on your exemple)
Add the removal feature on the delete endpoint.
I have the following two models:
class Timestampable(models.Model):
created_at = models.DateTimeField(null=True, default=None)
updated_at = models.DateTimeField(null=True, default=None)
class Meta:
abstract = True
def save(self, *args, **kwargs):
now = timezone.now()
if not self.created_at:
self.created_at = now
self.updated_at = now
super(Timestampable, self).save(*args, **kwargs)
class Attachment(Timestampable, models.Model):
uuid = models.CharField(max_length=64, unique=True)
customer = models.CharField(max_length=64)
user = models.CharField(max_length=64)
file = models.FileField(upload_to=upload_to)
filename = models.CharField(max_length=255)
mime = models.CharField(max_length=255)
publicly_accessible = models.BooleanField(default=False)
When I try to migrate these models, I get the following error:
django.core.exceptions.FieldError: Local field 'created_at' in class 'Attachment' clashes with field of similar name from base class 'Timestampable'
I read here, here, and here that this should work when the base class is abstract. However, I marked it as abstract it still it doesn't seem to work. What else could be wrong? I am using Django 1.8.14.
I found what was the problem. I used to have the class Timestampable not inherit from models.Model. Therefore, in one of my initial migrations, I had the line:
bases=(at_common.behaviours.Timestampable, models.Model),
I was looking for a way to remove this. Turned out that I just had to delete this line from the initial migration file.
Because your Timestampable model already extends from models.Model. You don't need extend Attachment model.
please use:
class Attachment(Timestampable):
instead of:
class Attachment(Timestampable, models.Model):
I have short model definition
class File(models.Model):
id = models.IntegerField(primary_key=True);
file = models.FileField(upload_to='%id')
title = models.CharField(max_length=128)
upload_date = models.DateTimeField(auto_now_add=True, blank=True);
as u see (or not) I would like this model to handle upload so file name would be the same as row id. Is it possible to do it this way somehow?
sure you can
def update_filename(instance, filename):
filename_ = instance.id
file_extension = filename.split('.')[-1]
return '%s.%s' % (filename_, file_extension)
class File(models.Model):
id = models.IntegerField(primary_key=True)
file = models.FileField(upload_to=update_filename)
title = models.CharField(max_length=128)
upload_date = models.DateTimeField(auto_now_add=True, blank=True)
and I would change the class name to something else, so it does not shadow the built in File.
I have the following models in Django:
class campaign(models.Model):
start_date = models.DateField('Start Date')
end_date = models.DateField('End Date')
description = models.CharField(max_length=500)
name = models.CharField(max_length=200)
active_start_time = models.TimeField()
active_end_time = models.TimeField()
last_updated = models.DateTimeField('Date updated',auto_now=True)
active = models.BooleanField(default=True)
client_id = models.ForeignKey('client',on_delete=models.PROTECT)
def __unicode__(self):
return u'%d | %s | %s' % (self.id,self.name, self.description)
class campaign_product(models.Model):
product_id = models.ForeignKey('product',on_delete=models.PROTECT)
last_updated = models.DateTimeField('Date updated',auto_now=True)
campaign_id = models.ForeignKey('campaign',on_delete=models.PROTECT)
class product(models.Model):
name = models.CharField(max_length=200)
description = models.CharField(max_length=500)
sku = models.CharField(max_length=200,blank=True,null=True)
retail_price = models.DecimalField(decimal_places=2,max_digits=11)
discount_price = ((1,'Yes'),(0,'No'))
discounted_price = models.DecimalField(decimal_places=2,max_digits=11,blank=True,null=True)
category_id = models.ForeignKey('category',on_delete=models.PROTECT)
last_updated = models.DateTimeField('Date updated',auto_now=True)
active = models.BooleanField(default=True)
def __unicode__(self):
return u'%d | %s' % (self.id, self.name)
I also have the following serializer:
class campaignProductSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = campaign_product
fields = ('product_id', 'campaign_id')
And the following view set behavior in the urls.py file:
class campaignProductViewSet(viewsets.ModelViewSet):
queryset = campaign_product.objects.filter(campaign_id__start_date__lte=datetime.now(),campaign_id__end_date__gte=datetime.now(),campaign_id__active__exact=True)
serializer_class = campaignProductSerializer
My problem is I need to include the name field from the products model in my query results when for instance a request is made on http://127.0.0.1:8000/campaign_product/1/. Currenly this request returns only the product_id and the campaign_id. I tried making the serializer as follows:
class campaignProductSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = campaign_product
fields = ('product_id', 'campaign_id', 'product.name')
But then the service returns the following error:
Field name `product.name` is not valid for model `campaign_product`.
I event tried using product__name with and without quotes. Without quotes it tells me that there is no such variable, and with quotes it gives the is not valid for model error similar to the above. Heeelp! Getting this extra field is proving to be a pain :-(
What you want will need to look something more like this:
class campaignProductSerializer(serializers.HyperlinkedModelSerializer):
product_name = serializers.CharField(source='product_id.name')
class Meta:
model = campaign_product
fields = ('product_id', 'campaign_id', 'product_name')
P.S. As an unrelated side note, it is generally a convention in Python code to name classes with CamelCase, such as Campaign, CampaignProduct, Product, and CampaignProductSerializer.
Edit: P.P.S. Originally, I had put written the product_name field with source='product.name'. This was actually due to me looking at the code too quickly and making assumptions based on Django conventions. Typically, with a Django ForeignKey, you would name the ForeignKey field after the model you are linking to, rather than explicitly naming it with _id. For example, the CampaignProduct model would typically be written with product = ForeignKey(...) and campaign = ForeignKey(...). In the background, Django will actually use product_id and campaign_id as the database field names. You also have access to those names on your model instances. But the product and campaign variables on your model instances actually return the objects which you are referring to. Hopefully that all makes sense.
I'm struck on saving m2m relation.
models.py
class BasicTag(models.Model):
name = models.CharField(max_length=150, verbose_name="Tag Name")
image_class = models.CharField(max_length=30, blank=True)
color = models.CharField(max_length=10, blank=True)
def __unicode__(self):
return self.name
class ExtendedTag(models.Model):
parent = models.ManyToManyField(BasicTag, blank=True,
related_name="parent")
category = models.ManyToManyField(BasicTag, blank=True,
related_name="category")
def __unicode__(self):
return self._id
class CombineTag(BasicTag, ExtendedTag):
"""
"""
forms.py
class CombineTagForm(forms.ModelForm):
class Meta:
model = CombineTag
Now when I initialize form as
>form = CombineTagForm({"name":"shyam"})#this don't have any parent i.e. root tag
>form.is_valid()
True
>instance = form.save(commit = False)
>instance.save()
>form.save() #return errors
#error
ProgrammingError: column tag_extendedtag_parent.basictag_id does not exist
LINE 1: DELETE FROM "tag_extendedtag_parent" WHERE "tag_extendedtag_...
>form.save_m2m() #return errors ... struck here
So How should I need to save m2m field modelform. I had follow official doc which say that
If your model has a many-to-many relation and you specify commit=False when you save a form, Django cannot immediately save the form data for the many-to-many relation. This is because it isn’t possible to save many-to-many data for an instance until the instance exists in the database.
But I couldn't able to figure out what I am missing here.