Accessing model from FileSystemStorage - python

I have custom FileSystemStorage.
The idea is to pass optional filename parameter.
My custome storge code:
class SalesMapFileStores(FileSystemStorage):
def __init__(self, location=None, base_url=None, filename=None):
if filename:
self.filename = filename
super(SalesMapFileStores, self).__init__(location=location, base_url=base_url)
def get_available_name(self, name):
return name
def get_valid_name(self, name):
if self.filename:
return self.filename
return name
def _save(self, name, content):
if self.exists(name):
self.delete(name)
return super(SalesMapFileStores, self)._save(name, conten
What i whant is to pass this filename parameter from model.
Somethin like this:
class SalesMapImage(models.Model):
name = models.CharField(max_length=254, verbose_name='Filename')
image = SalesMapImageField(upload_to='SalesMap/Test', storage=SalesMapFileStores(filename=name), verbose_name='Test Image',
content_types=('image/jpeg', 'image/png'))
But in this case, Django passes as a parameter itself model.CharField (it's obvious :)).
The question is: how can I get access to my model instance from Storage?
Thanks in advance!

Well, it is a bit crazy idea but you can try to override the assignment to that field in that class so the SalesMapFileStores instance always keep in sync with the name field like this:
class SalesMapImage(models.Model):
name = models.CharField(max_length=254, verbose_name='Filename')
image = SalesMapImageField(upload_to='SalesMap/Test', storage=SalesMapFileStores(), verbose_name='Test Image',
content_types=('image/jpeg', 'image/png'))
def __setattr__(self, key, value):
self.__dict__[key]=value
if key=='name':
self.image.storage.filename = value
And the general idea is to hook the assignment of the value to the update of the filename field.
This is supposing that you don't want to update it manually from within your view. Because it wouldn't take much effort to do model_instance.storage.filename = self.name or even add a method to your custom storage class to update the filename.
UPDATE: Heads up for the storage=SalesMapFileStores(). There you are passing a instance of SalesMapFileStores. Not the class, so it might be possible you'll be using the same instace for storing all files and this my bring conflicts with filename. You can try it like this: storage=SalesMapFileStores
Hope this helps!

Related

Share variables within the instance of a class without adding them as attributes

I need to create an instance specific URL based on the argument given to create the instance. This URL has to be available to all methods of my class, but I don't want the URL to be an attribute of the instance itself.
This is what I have:
class Person(object):
def __init__(self, name):
self.name = name
self.url = f'https://stackoverflow/{name}/'
def methodA(self):
self.result1 = parse(self.url, do sth)
def methodB(self):
self.result2 = parse(self.url, do sth else)
This would be an improvement but wouldn't fulfill the DRY principle:
class Person(object):
def __init__(self, name):
self.name = name
def methodA(self):
url = f'https://stackoverflow/{self.name}/'
self.result1 = parse(url, do sth)
def methodB(self):
url = f'https://stackoverflow/{self.name}/'
self.result2 = parse(url, do sth else)
Isn't there something in between?
I thought about defining a method which deletes unwanted runtime attributes after adding them to self, but that's probably not best practice?
For the context: The example above is heavily simplified. The real world example is about several parsed objects of the response which are being used multiple times.
In addition to making it private, as #plalx mentioned, it seems like you also want to make it dynamic relative to self.name, which you can do by making it a property. For example:
class Person(object):
def __init__(self, name):
self.name = name
#property
def _url(self):
return f'https://stackoverflow/{self.name}/'
def methodA(self):
self.result1 = parse(self._url)
def methodB(self):
self.result2 = parse(self._url)
Unfortunately there's no access modifiers in Python so you can't make the variable private to the class like you would in languages like Java for instance. However, you can still indicate that it's meant to be an internal variable by using the _ or __ prefixes.
Another option would be to nest your class declaration within an enclosing function which is an approach commonly used to create private scopes in JavaScript, but since the URL is dynamic based on the name you'd have to re-compute it every time:
def Person(name):
def urlOf(person):
return f'https://stackoverflow/{person.name}/'
class Person(object):
def __init__(self, name):
self.name = name
def test(self):
print(urlOf(self));
return Person(name)
Person('test').test()

Use model attribute as field parameter

I'm trying to create a folder and upload an image to in it. My model is similar to this:
class XYZ(models.Model):
def code(self):
syms = ['#','#','$','%','&','*','+','-']
return ''.join(x+random.choice(syms) for x in [self.name[-2:],self.name[2:]])
def make_folder(self):
os.mkdir(os.getcwd()+'/XYZ/'+folder)
def save(self):
self.make_folder()
super(XYZ,self).save(*args,**kwargs)
name = models.CharField(max_length=20)
folder = property(code)
image = models.ImageField(upload_to='HELP_HERE')
I've tried using folder, self.folder, property(code) but it says it isn't defined, how do I access this attribute? Is this the correct approach?
I've also set in the configs.py my MEDIA_URL and MEDIA_ROOT
I think a more simple and understandable way to do this would be to avoid using the inner class function and doing something like this:
# Reference this function outside of your model class.
def xyz_directory_path(instance, filename):
# file will be uploaded to MEDIA_ROOT/xyz_<id>/<filename>
return 'xyz_{0}/{1}'.format(instance.id, filename)
Then, on your models, reference the function in your upload_to field like so:
class XYZ(models.Model):
image = models.ImageField(upload_to=xyz_directory_path)
With your media config set up, this should work. There's a really nice and simple explanation about file handling here.

Why Django generate new migrations file each time I use `makemigrations` command. For ImageFIeld with changed upload_path attribute

I need to get ImageField name in upload_path function.
I tried use partial in ImageField definition:
class MyModel(models.Model):
image = models.ImageField(
upload_to=partial(image_upload_path, 'image')
)
Now I can get that string by first argument of function:
def image_upload_path(field, instance, filename):
....
All works fine, but now Django generate migration file, each time I use makemigrations, with same operations list in it:
operations = [
migrations.AlterField(
model_name='genericimage',
name='image',
field=core_apps.generic_image.fields.SorlImageField(upload_to=functools.partial(core_apps.generic_image.path.image_upload_path, *('image',), **{}),),
),
]
Maybe there is another way to access Field name in upload_path function or somehow I can fix my solution?
It seems like you don't need to provide a partial in this case, but just a callable with two parameters like in this example in the Django documentation.
Django will invoke the callable you provide in the upload_to argument with 2 parameters (instance and filename).
instance:
An instance of the model where the FileField is defined. More specifically, this is the particular instance where the current file is being attached.
This means you can access the name field of the instance like instance.name in the callable you write:
class MyModel(models.Model):
name = models.CharField(max_length=255)
image = models.ImageField(upload_to=image_upload_path)
def image_upload_path(instance, filename):
# Access the value of the `name` field
# of the MyModel instance passed in and save it to a variable:
name = instance.name
# Code that returns a Unix-style path (with forward slashes) goes here
I decide to build my own field:
class SorlImageField(ImageField):
def __init__(self, verbose_name=None, name=None, width_field=None,
height_field=None, lookup_name=None, **kwargs):
self.lookup_name = lookup_name
kwargs['upload_to'] = partial(image_upload_path, lookup_name)
super(SorlImageField, self).__init__(verbose_name, name,
width_field, height_field, **kwargs)
def deconstruct(self):
name, path, args, kwargs = super(SorlImageField, self).deconstruct()
del kwargs['upload_to']
# del upload_to will solve migration issue
return name, path, args, kwargs
def check(self, **kwargs):
errors = super(SorlImageField, self).check(**kwargs)
if self.lookup_name != self.name:
error = [
checks.Error(
'SorlImageField lookup_name must be equal to '
'field name, now it is: "{}"'.format(self.lookup_name),
hint='Add lookup_name in SorlImageField',
obj=self,
id='fields.E210',
)]
errors.extend(error)
return errors
Problem with migration was solved in deconstruct method, by deleting upload_to argument. Also I add additional argument into __init__ which point to field name, check function check for correct lookup_name value. If it not, it will raise an error when migrations starts.
class MyModel(models.Model):
image = SorlImageField(
lookup_name='image'
)

what does describe do in python?

I was going through code of https://github.com/hit9/CURD.py/blob/master/CURD.py which is a simple orm that performs normal curd operations .. and i could not understood part of code which goes like this(on line number 616):
.....#smthing #..
for name, attr in cls.__dict__.iteritems():
if isinstance(attr, Field):
attr.describe(name, cls)
fields[name] = attr
what does attr.describe(attr, Field) do ? I googled it out but found nothing.
It's not a Python language feature, it's a method on that library. You can see the definition here:
https://github.com/hit9/CURD.py/blob/master/CURD.py#L251
class Field(Leaf):
"""
Field object.
Field examples: User.name, User.age ..
"""
def __init__(self, is_primarykey=False, is_foreignkey=False):
self.is_primarykey = is_primarykey
self.is_foreignkey = is_foreignkey
# describe model's attr
def describe(self, name, model):
self.name = name
self.model = model
# fullname e.g. : User.id 's fullname is "user.id"
self.fullname = self.model.table_name + "." + self.name
# describe the attribute, reload its access control of writing, reading
setattr(model, name, FieldDescriptor(self))
setattr sets an attribute on an object. So if I call describe("field_name", myObject), it will set myObject.field_name to the description of the model. Or something like that.
That is not a python standard thing.
The loop iterates through the names and values of a class, and the attributes of that class that are instances of a Field type are added to a dictionary.
Field is not part of python standard library, you should search that project for the Field class.

python: Accessing an instance variable using a name containing a variable

in python I'm trying to access a instance variable where I need to use the value of another variable to determine the name: Example Instance Variable: user.remote.directory where it point to the value of 'servername:/mnt/.....' and user portion contains the userid of the user, such as joe.remote.directory
from another class I need to be able to access the joe.remote.directory using a variable that contains the user id of joe. I tried variable.remote.directory but it doesn't work, any suggestions?
Unsure quite what you want, but I think getattr(obj, 'name') might help. See http://docs.python.org/library/functions.html#getattr
You can refer to instance variable called name of object obj this way:
obj.__dict__['name']
Therefore, if you have another variable prop which holds the name of the instance variable you'd like to refer to, you can do it this way:
obj.__dict__[prop]
If you find yourself needing this functionality, you should ask yourself whether it isn't in fact a good circumstance to use an instance of dict instead.
I would suggest you create an extra User-Object, which you pass to the appropriate Objects or functions as needed. You are being extremly vague, so it's hard to give you a more practical advice.
Example:
class User:
def __init__(self, name, uid=None, remote=None, dir=None):
self.name = name
self.uid = uid
self.remote = remote
self.directory = dir
def get_X(self)
...
def create_some_curios_String(self):
""" for uid = 'joe', remote='localhost' and directory = '/mnt/srv'
this method would return the string:
'joe#localhost://mnt/srv'
"""
return '%s#%s:/%s' % (self.uid, self.remote, self.directory)
class AnotherClass:
def __init__(self, user_obj):
self.user = user_obj
class YetAnotherClass:
def getServiceOrFunctionalityForUser(self, user):
doWhatEverNeedsToBeDoneWithUser(user)
doWhatEverNeedsToBeDoneWithUserUIDandRemote(user.uid, user.remote)
joe = User('Joe Smith', 'joe', 'localhost', '/mnt/srv')
srv_service = ServerService(joe.create_some_curios_String())
srv_service.do_something_super_important()

Categories