Django: changing model's schema (replace CharField with ImageField) - django

I modify project based on Django framework. I have form to add an item. Item has the cover (an image). Current version of model for this item store cover's url like this:
class Item(models.Model):
title = models.CharField(max_length = 255, db_index = True)
slug = models.CharField(max_length = 80, db_index = True)
categories = models.ManyToManyField(Category)
cover_url = models.CharField(max_length = 255, null = True, default = None)
...
Important notice that some images stored on other servers (different file hosting).
I want to replace CharField with ImageField. But how about existing items? I want to change model's schema and save all previously added images. How I can achieve this goal?
Maybe some reasons for this modification can be helpful. The main reason is to provide to users ability to upload images from their computer (not only insert the urls as it was).
TIA!

If cover_url can have existing source - you must have custom storage, that can handle external sources.
Here is example of custom storage usage for ImageField from django documentation:
from django.db import models
from django.core.files.storage import FileSystemStorage
fs = FileSystemStorage(location='/media/photos')
class Car(models.Model):
...
photo = models.ImageField(storage=fs)
Let's jump out of it and we will get code like this:
from django.db import models
from django.core.files.storage import FileSystemStorage
def is_url(name):
return 'http' in name
class MyStorage(FileSystemStorage):
#We should override _save method, instead of save.
def _save(self, name, content=None):
if content is None and is_url(name):
return name
super(MyStorage, self)._save(name, content)
fs = MyStorage()
class Item(models.Model):
title = models.CharField(max_length = 255, db_index = True)
slug = models.CharField(max_length = 80, db_index = True)
categories = models.ManyToManyField(Category)
cover_url = models.ImageField(storage=fs)
It has big room for improvements - here is shown only idea.

Related

Django models for astra datastax db

I have developed a website on Django. Initially, I used Django's default Database which is Sqlite3. Now I want to use Astra Datastax DB which is Cassandra. I am not able to convert Django.dB - models into Cassandra.cqlengine - columns function.
I have searched on the Internet and didn't find appropriate documents which could help me.
from django.db import models
from django.contrib.auth import get_user_model
from datetime import datetime
import uuid
User = get_user_model()
class Profile(models.Model):
"""docstring for Profile."""
usr: str = models.ForeignKey(User, on_delete=models.CASCADE)
id_usr: int = models.IntegerField()
Fname:str = models.TextField(blank=True,null=True)
Mname:str = models.TextField(blank=True,null=True)
Lname:str = models.TextField(blank=True,null=True)
Fhone:int = models.IntegerField(blank=True,null=True)
bio: str = models.TextField(blank=True)
img_profile = models.ImageField(
upload_to='ProfileIMG', default="blankprofile.png")
location: str = models.CharField(max_length=250)
def __str__(self):
return self.usr.username
class Post(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, unique=True)
user: str = models.CharField(max_length=100)
image = models.ImageField(upload_to="img_posts")
caption: str = models.TextField(max_length=250)
created_at = models.DateTimeField(default=datetime.now)
Likes: int = models.IntegerField(default=0)
def __str__(self):
return self.user
class LikePost(models.Model):
postid: str = models.CharField(max_length=100)
username: str = models.CharField(max_length=100)
def __str__(self):
return self.username
class Followers(models.Model):
follower: str = models.CharField(max_length=100)
user: str = models.CharField(max_length=100)
def __str__(self):
return self.user
Specially, I want to convert this into Cassandra language.
img_profile = models.ImageField( upload_to='ProfileIMG', default="blankprofile.png")
The documentation you want to consult is here: https://docs.datastax.com/en/developer/python-driver/3.25/api/cassandra/cqlengine/columns/
These are the available columns for DjangoCassandraModel, which is what you would use instead of django.db.models to get a model backed by Cassandra. You can see a basic example of connecting to DataStax Astra with django_cassandra_engine here: https://github.com/DataStax-Examples/django-cassandra-blog
As for the ImageField, I am not sure of the Django internals here, but I believe it stores a path to the file in the database after putting it on disk at the location specified ("img_posts" in your example).
You could do the same for a Cassandra backed model, or use the Blob column type to store the image data itself. There are a number of articles and examples of doing this out there.
To add to the above answer, the django_cassandra_engine package does not offer anything with the same level of automation as the ImageField found in Django's models (i.e. storing the uploaded image on local disk and saving the string path to the database, all from the field definition in the model).
What you would do is to work at a slightly lower abstraction level with an explicit (Django, pure) form. This would allow you to manually handle the file upload as outlined here: https://docs.djangoproject.com/en/4.1/ref/forms/fields/#filefield .
Once you have saved the file and have the string path to it, you can create ann istance of the corresponding Model and manually save it -- all in the appopriate view function.
As a side note, your original (sqlite-backed) code makes use of foreign keys and "on delete cascade" provision for removing related rows from other tables. This cannot be transported as is to a Cassandra storage, since the database, by itself, does not support the concept of relational integrity. You would have to decide how to handle these deletes and act on them explicitly in your code.

Storing restaurant categories in GeoDjango project

I am making a geo django backend for an iPhone app. The iphone app sends users current location to the backend, which returns nearby restaurants (something similar to Foursquare and Yelp.)
I am not entirely sure how to store the cuisine for the restaurants. I need an option where the user can look up/select a specific cuisine for example only Chinese food. Should I create a seperate model for restaurant cuisine and have one to many relationship with the restaurants ? or should I use boolean value like this:
models.BooleanField(default=False)
Here is the code for my Model:
from django.db import models
from django.contrib.gis.db import models as gis_models
from django.contrib.gis import geos
from django.db import models
# Create your models here.
class Restaurant(models.Model):
name = models.CharField(max_length = 100)
address = models.CharField(max_length = 150)
phone = models.CharField(max_length = 12)
cuisine = models.CharField(max_length = 50)
eatingOptions = models.CharField(max_length = 50)
location = gis_models.PointField(u'Latitude/Longitude', geography=True, blank=True, null=True)
# Query Manager
gis = gis_models.GeoManager()
objects = models.Manager()
def __unicode__(self):
return self.name
Only you and your requirements can answer that.
If you create a separate model and have a relationship it will be more flexible, you can have different cuisines without changing the model. Best option if your database will have multiple types of cuisine.
If you choose to use a BooleanField like is_chinese then it will work only for chinese cuisine. This is the best option if you only care for one type of cuisine.
Don't worry if you make a bad judgement when developing (at least on an early stage), django has your back.
Hope that helps.

Django entry creating. Method in views/other file or as a method of model class

Django creating entry.
1) As seen in Django docs:
class Article(models.Model):
user = models.ForeignField(User)
title = models.CharField(#some_params)
content = models.CharField(#some_params)
date = models.DateTimeField(#some_params)
Then in my views I can:
new_article = Article(user=user, title="abc", content="xyz", date = datetime.utcnow())
new_article.save()
2) But it also can be done like by calling method within Article class, ie:
class Article(models.Model):
user = models.ForeignField(User)
title = models.CharField()
content = models.CharField()
def add_article(self, title, content):
self.title = title
self.content = content
self.date = datetime.utcnow()
self.save()
and then in views:
title = "abc"
content = "xyz"
new_article = Article(user=user)
new_article.add_article(abc, xyz)
I am asking because I have seen both ways of adding content into database. I would like to ask:
What is better practice?
Any concerns about security in 2nd example?
Option 1 is, in my opinion, better practice. add_article is unnecessary, because you can set the date by using the auto_now property of the date field, and everything else is already built-in. You'll have less code and less maintenance.

Pathname to the image field in Django

I have this model which has Image field to be uploaded. It has a foreign key reference to another class.
from django.template.defaultfilters import slugify
def upload_to(path, attribute):
def upload_callback(instance, filename):
return '%s%s/%s' % (path, unicode(slugify(getattr(instance, attribute))), filename)
return upload_callback
class Data(models.Model):
place = models.CharField(max_length=40)
typeOfProperty = models.CharField(max_length=30)
typeOfPlace = models.CharField(max_length=20)
price = models.IntegerField()
ownerName = models.CharField(max_length=80)
class ImageData(models.Model):
property = models.ForeignKey(Data, related_name='images')
image = models.ImageField(upload_to = upload_to('image/', 'ownerName'),blank=True,null=True)
def __unicode__(self):
return self.property.ownerName
I have refered this This Web Page to create a dynamic field for images to be stored.
My doubt is can I use the onerName as the attribute in (as the ownerName is in the super class) :
image = models.ImageField(upload_to = upload_to('image/', 'ownerName'),blank=True,null=True)
How does Django consider this request that is need to be served?
Please can anyone explain me this?
'ownerName' is not going to work. It's quite complicated to do the definition of what you want to save in the ImageField directly. Maybe you should do something like this:
def upload_to(path):
def upload_callback(instance, filename):
return '%s%s/%s' % (path, unicode(slugify(instance.property.ownerName), filename)
return upload_callback
If you really want to make it as dynamic as possible you have to pass something like 'property.ownerName' to the function, split the string, retrieve attrtibute property from ImageData instance and then attribute ownerName from its foreign key instance.
Though I think this makes things way to complicated and you better define extra functions for different use cases.

Automatically update images

I'd like to implement a functionality in an app of mine, but I don't know how to go about it. What I want is this: I have a model class that uses imagekit to save its images, and I'd like to have the users being able to update the images easily for the vehicles without having to edit each respective vehicle record.
How they'll do this is that there will be a folder named originals and it'll contain folders for each vehicle in the format <stock_number>/PUBLIC If a user moves images into the PUBLIC folder for a vehicle, when the script is executed, it'll compare those images with the current ones and update them if those in the PUBLIC folder are newer. If the record has no images, then they will be added. Also, if the images have been deleted from the site_media directory, then their links should be deleted from the database.
How can I go about this in an efficient way? My models are as below:
class Photo(ImageModel):
name = models.CharField(max_length = 100)
original_image = models.ImageField(upload_to = 'photos')
num_views = models.PositiveIntegerField(editable = False, default=0)
position = models.ForeignKey(PhotoPosition)
content_type = models.ForeignKey(ContentType)
object_id = models.PositiveIntegerField()
content_object = generic.GenericForeignKey('content_type', 'object_id')
class IKOptions:
spec_module = 'vehicles.specs'
cache_dir = 'photos'
image_field = 'original_image'
save_count_as = 'num_views'
class Vehicle(models.Model):
objects = VehicleManager()
stock_number = models.CharField(max_length=6, blank=False, unique=True)
vin = models.CharField(max_length=17, blank=False)
....
images = generic.GenericRelation('Photo', blank=True, null=True)
Progress Update
I've tried out the code, and while it works, I'm missing something as I can get the image, but after that, they aren't transferred into the site_media/photos directory...am I suppossed to do this or imagekit will do this automatically? I'm a bit confused.
I'm saving the photos like so:
Photo.objects.create(content_object = vehicle, object_id = vehicle.id,
original_image = file)
My advice is running django script in a crontab job, lets say, 5 in 5 minutes.
The script would dive into the image folders and compare the images with the records.
A simplified example:
# Set up the Django Enviroment
from django.core.management import setup_environ
import settings
setup_environ(settings)
import os
from your_project.your_app.models import *
from datetime import datetime
vehicles_root = '/home/vehicles'
for stock_number in os.listdir(vehicles_root):
cur_path = vehicles_root+'/'+stock_number
if not os.path.isdir(cur_path):
continue # skip non dirs
for file in os.listdir(cur_path):
if not isfile(cur_path+'/'+file):
continue # skip non file
ext = file.split('.')[-1]
if ext.lower() not in ('png','gif','jpg',):
continue # skip non image
last_mod = os.stat(cur_path+'/'+file).st_mtime
v = Vehicle.objects.get(stock_number=stock_number)
if v.last_upd < datetime.fromtimestamp(last_mod):
# do your magic here, move image, etc.
v.last_upd = datetime.now()
v.save()