Django make non persistent field when file uploading - python

I have a model for Document like :
class Document(models.Model):
document_name = models.CharField(max_length=64)
author = models.ForeignKey(Client, null=False, default=1)
size = models.IntegerField(default=0)
version = models.CharField(max_length=64)
file = models.FileField(upload_to='documents/%Y/%m/%d')
but I also want to upload that file. My form looks like
class UploadDocumentForm(ModelForm):
class Meta:
model = Document
fields = ('document_name', 'author', 'size', 'version', 'file',)
def __init__(self, *args, **kwargs):
super(UploadDocumentForm, self).__init__(*args, **kwargs)
Now obviously, I want my file to be saved in a folder and not have its own column i nthe db. In db I only want the other data. Problem is, django tries to persist it and crashes with
The above exception (table document has no column named file)
Is there a way to make file a non persisted field or something?
Im following this tutorial, https://simpleisbetterthancomplex.com/tutorial/2016/08/01/how-to-upload-files-with-django.html
I tried removing file form the model and adding it just in the form somehow but it doesn't work. It is not recognized liek that. I just want it to not be persisted in the sqlite database. Any ideas?
I thought about making a second model without the file and create somehow both of them or override the save method.. Im not sure how to make it work.

Related

Create model instance out of Form and add it to other instance

In my models.py I have model called Bus. It contains multiple fields, including the field file below:
class Bus(models.Model):
file = models.OneToOneField('File', on_delete=models.PROTECT, editable=False)
The File model consists of:
class File(models.Model):
file = models.FileField(
upload_to=get_filepath,
max_length=45,
validators=[
FileExtensionValidator(allowed_extensions=['pdf', 'jpg', 'png']),
file_size,
]
)
original_name = models.CharField(max_length=45)
extension = models.CharField(max_length=3)
When you want to create a new Bus I need of course a form. That's defined in my forms.py:
class BusForm(forms.ModelForm):
upload = forms.FileField(max_length=45, required=False,
validators=[
FileExtensionValidator(allowed_extensions=['pdf', 'jpg', 'png']),
file_size,
],)
My problem:
In the save() method of BusForm I have to create a File instance and add it to the Bus instance (file field).
For multiple hours I tried to achieve that. However, I don't get it. If someone could try to help me how the save() method has to look like, that would be great!
You can create save method like this
def save(self, commit=False):
# This will create bus_instance but not save in database
bus_instance = super(self, BusForm).save(commit=False)
# create file instance
file_instance = File.objects.create() # add the required parameters
# Assign file_instance to file attribute of bus_instance
bus_instance.file = file_instance
# Save to database only if user calling save method wants to save in database
if commit:
bus_instance.save()
return bus_instance

save upload file to dynamic directory and get the path for store in db

I'm uploading a file and additionally some data like file's id and file's title to the server. I have the view below to handle the request and I want to save the file to a dynamic path like upload/user_id/thefile.txt.
With code the below the file will be saved in and upload folder directly and also my product_video table will create a new record with the related id and the title.
Now I don't have any idea how can I save the file in a dynamically generated directory like: upload/user_id/thefile.txt and how to save the produced path to the database table column video_path?
view class:
class FileView(APIView):
parser_classes = (MultiPartParser, FormParser)
def post(self, request, *args, **kwargs):
if request.method == 'POST' and request.FILES['file']:
myfile = request.FILES['file']
serilizer = VideoSerializer(data=request.data)
if serilizer.is_valid():
serilizer.save()
fs = FileSystemStorage()
fs.save(myfile.name, myfile)
return Response("ok")
return Response("bad")
and serializer clas:
class VideoSerializer(ModelSerializer):
class Meta:
model = Product_Video
fields = [
'p_id',
'title',
'video_length',
'is_free',
]
and related model class:
def user_directory_path(instance, filename):
return 'user_{0}/{1}'.format(instance.user.id, filename)
class Product_Video(models.Model):
p_id = models.ForeignKey(Product, on_delete=models.CASCADE, to_field='product_id', related_name='product_video')
title = models.CharField(max_length=120, null=True,blank=True)
video_path = models.FileField(null=True, upload_to=user_directory_path,storage=FileSystemStorage)
video_length = models.CharField(max_length=20, null=True, blank=True)
is_free = models.BooleanField(default=False)
You put the cart before the horse. Start from the beginning, first things first. And the first thing is the user story, then the model layer.
There are Products and a Product can have many ProductVideos. A Product has an Author (the Author can have many Products). When you upload a ProductVideo for a particular Product, you want to save it in a directory which contains the Author ID.
Therefore we specify a callable for the FileField which should dynamically find out the id of the Author:
def user_directory_path(instance, filename):
# get the id of the author
# first get the Product, then get its author's id
user_id = str(instance.p_id.author.id)
# little bit cosmetics
# should the filename be in uppercase
filename = filename.lower()
return 'user_{0}/{1}'.format(user_id, filename)
When saving an instance of Product_Video the uploaded file should be stored in a directory with a dynamically created pathname based on the author's ID.
Further I'd suggest you to follow established coding conventions:
don't use snake_case for class names, especially don't mix it with capital letters. Use PascalCase, preferably singular nouns, for class names: ProductVideo
if your model class is ProductVideo, keep that name in all subsequent classes: ProductVideoSerializer for the model serializer and ProductVideoAPIView for the generic view.
use verbs for methods and standalone functions, get_user_directory or upload_to_custom_path is better than user_directory_path.
also avoid suffixing the foreign key fields with _id. Use rather product instead of p_id. Django will allow you to access the related object by calling product and get the id of the object by calling product_id. Using the name p_id means you'll access the id of the related object by calling p_id_id which looks very weird and confusing.

Django model save override not changing self

I'm stuck using a non-Django legacy MySQL database. I need to write code that generates a unique filename each time a model object is saved. The following doesn't work. It won't overwrite the filename field. It saves fine with whatever the filename field was set to. Is this because that field is set as the primary key?
(I realize my code isn't creating a random filename--haven't gotten that far yet. Also, I know this will only save once since it needs to be unique, but it won't even save the first time).
class Agenda(models.Model):
type = models.IntegerField()
filename = models.CharField(max_length=45, primary_key=True)
date = models.DateField()
class Meta:
managed = False
db_table = 'gbminutes'
def save(self, *args, **kwargs):
self.filename = 'ATESTFILE'
super(Agenda, self).save(*args, **kwargs)

Changing model field within the Django Shell

Is there anyway to use the Django shell to modify a field value? I can create, delete, and query models, but I don't know how to alter existing field values.
class Game(models.Model):
name = models.CharField(max_length=128, unique=True)
views = models.IntegerField(default=0)
likes = models.IntegerField(default=0)
slug = models.SlugField(unique=True)
def save(self, *args, **kwargs):
self.slug = slugify(self.name)
super(Game, self).save(*args, **kwargs)
def __str__(self):
return self.name
In the Django shell, I try Game.objects.get(name="testb").likes = 5, but it still outputs likes = 0 when I input Game.objects.get(name="testb").likes right afterwards.
You should save the changes,
game = Game.objects.get(name="testb")
game.likes = 5
game.save()
Calling Game.objects.get() retrieves the data from the database.
When you execute the statement Game.objects.get(name='test').likes = 5, you are retrieving the data from the database, creating a python object, and then setting a field on that object in memory.
Then, when you run Game.objects.get(name='test') again, you are re-pulling the data from the database and loading a python object into memory. Note that above, when you set likes to 5, you did that purely in memory and never saved the data to the database. This is why when you re-pull the data, likes is 0.
If you want the data to be persisted, you have to call game.save() after setting the likes field. This will enter the data into the database, so that the next time you retrieve it via .get(), your changes will have persisted.
If u need change all fields for all items, just try this:
from shop.models import Product
Product.objects.filter(recommend=True).update(recommend=False)
U can use this in Django Shell. Have a nice time :)

Django, defining extra model fields on db level instead of programming level

I am using Python 2.7 and Django 1.6.3
I want to define extra model field which is not actually in db table. I have a way which is defining a callable method with property annotation like;
class MyClass(models.Model):
my_field = models.CharField(max_length=50)
#property
def my_extra_field(self):
return self.my_field+'extra value'
This works fine to show it on admin change list pages. But the extra field is not on db level. It is being generated on programming level. Django asks it for every model object.
This cause me some troubles. My all admin change list pages have capability of exporting as excel or some other type. I am using admin query set to build that report. I have also jasper reports mechanism that works with SQL select queries. So, I, want to use the queryset to take this select query.
I think being able to define extra fields on db level is important for something. Not just for reason of mine. So, the question all about this.
Is there a way to define an extra custom fields on db level instead of programming level in Django.
Thank you!.
Edited
Adding it to admin list_filter is also another problem if it is not really a field. Django does not allow you to add it.
Could you create a new database field and then overwrite the save method to populate that field? I do that often to create a marked up version of a text field. For example:
class Dummmy(models.Model):
content = models.TextField()
content_html = models.TextField(editable=False, null=True)
def save(self, *args, **kwargs):
self.content_html = markdown(self.content)
super(Dummmy, self).save(*args, **kwargs)
So for you:
class MyClass(models.Model):
my_field = models.CharField(max_length=50)
my_extra_field = models.CharField(editable=False, null=True)
def save(self, *args, **kwargs):
self.my_extra_field = self.my_field + 'extra value'
super(MyClass, self).save(*args, **kwargs)

Categories