Django model save override not changing self - python

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)

Related

Django Models (dynamic?)

I am just starting Django Website;
I have deciced to go with a model (with SQLite DB), which have the following properties:
class Flow(models.Model):
owner = models.ForeignKey(User, on_delete=models.CASCADE,
verbose_name="Owner", default="ADMIN")
source = models.CharField(default='HTTP', choices=SOURCE_CHOICES, editable=True, max_length=12)
name = models.CharField(max_length=50, default=" ")
date = models.DateTimeField(verbose_name="Creation date")
I want to add others fields to this model depending on the source field value.
For example if the source field : 'File' is selected. I will create additionnal field (like file name, file directory ...) If 'Http' is selected, I will create a field URL.
Thus depending on the source field, I will have differents field and type.
I have read this kind of model is difficult to reprensent in Django; I am open minded to other kind of solution. My idea was to created as many model as the possible source value.
Even if you can create dynamic field in Django, you can't create dynamic column in Sqlite table FLOW.
If you plan to use same types of fields in different cases, you can create field with abstract name, for example path. That can be as URL as local file path.
In common way you need to create all columns for all choices for DB table.
class Flow(models.Model):
owner = models.ForeignKey(User, on_delete=models.CASCADE,
verbose_name="Owner", default="ADMIN")
source = models.CharField(default='HTTP', choices=SOURCE_CHOICES, editable=True, max_length=12)
name = models.CharField(max_length=50, default=" ")
date = models.DateTimeField(verbose_name="Creation date")
local_path = models.ImageField(upload_to='files', null=True, blank=True) # when source is file
url = models.URLField(null=True, blank=True) # when source is http
So local_path may be empty when you using HTTP source.
Inside view you can dynamically get (or set) value through serializer:
class FlowSerializer(serializers.ModelSerializer):
path = serializers.SerializerMethodField(method_name='get_path_value')
class Meta:
model = Flow
fields = ('owner', 'source', 'name', 'date', 'path')
def get_path_value(self, instance):
if instance.source == 'HTTP':
return instance.url
else:
return instance.local_path
So path will be different for different sources.
And maybe you will be need to install django rest framework for this solution.
EDIT1:
answering to question
So if I understand well, the best pratices should be to create 'blank'
columns
You definitely must to describe all columns in table (unless you using non Sql-like DB, such as MongoDB). So yes, create 'blank' columns, is only one possible way.
But you can override save method in model, for dynamically save fields:
class Flow(models.Model):
temp_path = None
path = models...
url = models...
choice = models...
def save(self, *args, **kwargs):
if choice == 'HTTP':
self.url = temp_path
else:
self.path = temp_path
super().save(*args, **kwargs)
Code above is just a quick idea. Not really working code.
You could implement a custom model field type for that, it helps you save a union object in one column in database. The raw type in database could be a JSON string or other serialized types, for the model field's usage, it's just a python native object!
Here is a piece of sample code: the main jobs are these two methods:
class HandField(models.Field):
# ...
def from_db_value(self, value, expression, connection):
if value is None:
return value
return parse_hand(value)
def to_python(self, value):
if isinstance(value, Hand):
return value
if value is None:
return value
return parse_hand(value)
Check the official docs for details.

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 make non persistent field when file uploading

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.

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 :)

Code based unique constraint Django Model

I have a Django model that looks like this:
class Categories(models.Model):
"""
Model for storing the categories
"""
name = models.CharField(max_length=8)
keywords = models.TextField()
spamwords = models.TextField()
translations = models.TextField()
def save(self, force_insert=False, force_update=False):
"""
Custom save method that converts the name to uppercase
"""
self.name = self.name.upper()
super(Categories, self).save(force_insert, force_update)
Whenever the data is inserted or updated. I'd like to check that that a record with same name doesn't exists. It's a unique constraint that I'd like to implement via code and not the DB. The amount of data in this table is minuscule so the the performance hit is not an issue. If there is an constraint violation, I'd like to raise one of Django's inbuilt constraint exceptions instead of creating a custom one.
Could someone how me the best/fastest way to accomplish this?
Thanks.
In your model definition you can tell Django that 'name' should be unique:
name = models.CharField(max_length=8, unique=True)
A django.db.IntegrityError will be raised if you attempt to save two records with the same name.
in the view
try:
Category.objects.get(name='name')
except Category.DoesNotExist:
# call the save method of model

Categories