Django - getting a path from a variable when saving models - python

My app has a simple process, the user uploads a gif file and then the gif is conveted to frames that are saved as objects. For the upload_to part of my gif, I run a function content_file_name() that uses uuid to create a folder path. I want the image frames to be saved to the same folder as the gif. Problem is, I can't set the path as a variable as much as I try. The variable needs to be defined first, but if I define it, it doesn't change no matter what I do. Here's what I got now:
currentFilePath = '' # Defining the variable
def content_file_name(instance, filename):
ext = ''.join(filename.split())[:-4]
foldername = "%s/%s" % (uuid.uuid4(), ext)
store_random_path('/'.join(['documents', str(foldername)])) # Running a function to change the variable
return '/'.join(['documents', str(foldername), filename])
def store_random_path(path):
currentFilePath = str(path) # The variable should be changed here
class Document(models.Model):
docfile = models.ImageField(upload_to=content_file_name)
def create_documentfiles(self):
gif = Image.open(self.docfile.path)
frames = [frame.copy() for frame in ImageSequence.Iterator(gif)]
basename, _ext = os.path.splitext(self.docfile.name)
for index, frame in enumerate(frames):
buffer = BytesIO()
frame.convert('RGB').save(fp=buffer, format='JPEG')
destname = "{}{}.png".format(basename, index)
finalImage = InMemoryUploadedFile(buffer, None, destname, 'image/jpeg', frame.tell, None)
imageToSave = DocumentImage(imagefile=finalImage)
imageToSave.save()
class DocumentImage(models.Model):
imagefile = models.ImageField(upload_to=currentFilePath) # Trying to save using the variable as path
image = models.ForeignKey(Document, related_name='Image', null=True, on_delete=models.CASCADE)
So, when the DocumentImage gets saved, it should have seen the variable as path, but it doesn't. Instead it just saves to the initially declared '' which is the root of my media file. Not sure if what I'm trying is possible in Python/Django. I've just started learing Python a month ago. Thank you for your time.

You are assigning to a global variable inside a function. You need to use the global keyword for that or you could refactor your code to not use any global variable (see Why are global variables evil?). This is not specific to Django.
The wrong way
global_var = ''
def function():
global_var = 'hello' # Local variable created due to assignment
Here global_var is still ''
The Correct way
global_var = ''
def function():
global global_var
global_var = "hello" # Assignment to the global variable
Here global_var is "Hello"

But all you've done is to set a local variable inside store_random_path. You haven't even returned that variable, let alone changed the global variable with the same name.
But you must not use global variables like this anyway. That would cause serious bugs as any future request would get the same value of the global variable. Don't do this.
I'm not sure why you are trying to set this variable in the first place. You have access to the Document, which contains the docfile which has its path correctly set. You should use that to calculate the path of the frames.

Related

calling variables from other functions by using self._variable, and why it seems to work and not work by random (there must be a reason)?

I have this snippet of code I'm trying to understand piece by piece.
Inside one class (called: 'Window') there's a whole lot of functions that reference each other by the method of self._variable
For example this loadFiles(self) function provides the self._filenames (second to last line):
def loadFiles(self):
if self.sourceDirectory.text():
initDir = self.sourceDirectory.text()
else:
initDir = str(Path.home())
files, filter = QFileDialog.getOpenFileNames(
self, "Choose Files to Rename", initDir, filter=FILTERS
)
if len(files) > 0:
sourceDirectory = str(Path(files[0]).parent)
self.sourceDirectory.setText(sourceDirectory)
for file in files:
self._files.append(Path(file))
self._filesCount = len(self._files)
self._filenames = files
self.current_index = 0
And this function below can easily read that variable, it's in the topmost row (the self._current_index comes elsewhere from the code). But I just don't understand, why the self._variables (last line) that I make don't work? When I try to call my variable into another function with line extension = self._test I get this AttributeError: 'Window' object has no attribute '_test':
def renameFiles(self):
filename = self._filenames[self._current_index]
newFilename = self.createNewFilename()
self.currentFileName.setText(newFilename)
dirname = os.path.dirname(filename)
fullPath = os.path.join(dirname, newFilename)
os.replace(filename,fullPath)
self._filenames[self._current_index] = fullPath
self.current_index = self._current_index
self._test = fullPath
I hope you understand what I'm after here. I can provide more info if needed.
Here's the whole code in cleaned, working condition, except it needs the other .py files to function fully: https://gist.github.com/manujarvinen/fc727cd1d12b6ef2c708a40c32cc3ce1
If you want to check out all the files of this project, get them here (PyQt5 and Python3): https://files.manujarvinen.com/MyMaterial_Rename_Tool_v1.1.zip
Set the instance variables with a sentinel value inside the constructor, then inside your methods check them and make the program react accordingly, e.g.:
if self._test is None:
do_something
else:
do_else
If you don't want your __init__ to be a whole aggregation of temporary variables initialisations, you can check their existence calling hasattr(), e.g.:
if not hasattr(self, "_test"):
do_something
else:
do_else

How to access the variables of a class - Python

I have the following code to enable the file browser using blender:
import bpy
import os
from bpy.props import StringProperty
from bpy_extras.io_utils import ImportHelper
from bpy.types import Operator
sel = ''
class OpenBrowser(bpy.types.Operator):
bl_idname = "open.file"
bl_label = "Select Excel File"
bli_description = "Simulation output excel file"
filter_glob: StringProperty(default = '*.xls;*.xlsx',options = {'HIDDEN'})
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
#somewhere to remember the address of the file
def execute(self, context):
global sel
sel = self.filepath
#self.selected_file = self.filepath
#display = "filepath= "+self.filepath
#print(display) #Prints to console
#Window>>>Toggle systen console
return {'FINISHED'}
def invoke(self, context, event): # See comments at end [1]
context.window_manager.fileselect_add(self)
global sel
sel = self.filepath
#Open browser, take reference to 'self'
#read the path to selected file,
#put path in declared string type data structure self.filepath
return {'RUNNING_MODAL'}
# Tells Blender to hang on for the slow user input
bpy.utils.register_class(OpenBrowser)
#Tell Blender this exists and should be used
# [1] In this invoke(self, context, event) is being triggered by the below command
#but in your script you create a button or menu item. When it is clicked
# Blender runs invoke() automatically.
#execute(self,context) prints self.filepath as proof it works.. I hope.
bpy.ops.open.file('INVOKE_DEFAULT')
print(sel)
The issue I am facing is that I have declared a global variable sel to which I want to save the filepath selected from the user when running the code. However, when I run the script I see that sel has not changed and it is as it was initialized. Could someone please help me on how to access from the class the self.filepath variable? What am I doing wrong here?
If I understand correctly, you want to store that value for later.
I'm not sure why 'sel' doesn't even update in your case, but I think the more correct way would be to use a property like so:
import bpy
# Assign a custom property to an existing type.
bpy.types.Scene.my_sel_value = bpy.props.StringProperty(name="Sel")
# Set property value.
bpy.context.scene.my_sel_value = "Foo"
# Get property value.
print(bpy.context.scene.my_sel_value)
Properties can be added to all ID types, but for "global" values, bpy.types.scene us ussualy used. Though there can be multiple scenes in one project and they will have separate values. Property values are stored when Blender closes.
If you are making an addon, you can also store your value in Addon Preferences. This value will be the same for all blender projects.

Global variable not recognized in functions

I know that this question looks exactly like so many other on here, because I just read them all and they all say to do what I already tried, and it hasn't worked (or I'm missing a subtle difference with my situation). Here is my situation:
I am writing a scraper using Scrapy and Python 2.7.11, and my code looks like this (this is a copy and paste with irrelevant lines omitted, but I can re-add them upon request):
class LbcSubtopicSpider(scrapy.Spider):
...omitted...
rawTranscripts = []
rawTranslations = []
def parse(self, response):
#global rawTranscripts, rawTranslations
rawTitles = []
rawVideos = []
for sel in response.xpath('//ul[1]'): #only scrape the first list
...omitted...
index = 0
for sub in sel.xpath('li/ul/li/a'): #scrape the sublist items
index += 1
if index%2!=0: #odd numbered entries are the transcripts
transcriptLink = sub.xpath('#href').extract()
#url = response.urljoin(transcriptLink[0])
#yield scrapy.Request(url, callback=self.parse_transcript)
else: #even numbered entries are the translations
translationLink = sub.xpath('#href').extract()
url = response.urljoin(translationLink[0])
yield scrapy.Request(url, callback=self.parse_translation)
print rawTitles
print rawVideos
print rawTranslations
def parse_translation(self, response):
global rawTranslations
for sel in response.xpath('//p[not(#class)]'):
rawTranslation = sel.xpath('text()').extract()
rawTranslations.append(rawTranslation)
This will return an error any time either "print rawTranslations" or "rawTranslations.append(rawTranslation)" is called because the global "rawTranslations" is not defined.
As I said before, I have looked into this pretty extensively and pretty much everyone on the internet says to just add a "global (name)" line to the beginning of any function you'd use/modify it in (although I'm not assigning to it ever, so I shouldn't even need this). I get the same result whether or not my global lines are commented out. This behavior seems to defy everything I've read about how globals work in Python, so I suspect this might be a Scrapy quirk related to how parse functions are called through scrapy.Request(....).
Apologies for posting what appears to be the same question you've seen so much yet again, but it seems to be a bit twisted this time around and hopefully someone can get to the bottom of it. Thanks.
In your case the variable you want to access is not global, it is in the scope of the class.
global_var = "global"
class Example:
class_var = "class"
def __init__(self):
self.instance_var = "instance"
def check(self):
print(instance_var) # error
print(self.instance_var) # works
print(class_var) # error
print(self.class_var) # works, lookup goes "up" to the class
print(global_var) # works
print(self.global_var) # works not
You only need the global keyword if you want to write to a global variable. Hint: Don't do that because global variables that are written to bring nothing but pain and despair. Only use global variables as (config) constants.
global_var = "global"
class Example:
def ex1(self):
global_var = "local" # creates a new local variable named "global_var"
def ex2(self):
global global_var
global_var = "local" # changes the global variable
Example().ex1()
print(global_var) # will still be "global"
Example().ex2()
print(global_var) # willnow be "local"
if you want to use variable in class, you can use self.xxx
class A:
... var = []
... def test(self):
... self.var.append(10)
... print self.var

"SyntaxWarning: name 'our_mongo' is used prior to global declaration"

My code keeps giving me the above syntax warning. My code includes, but is not limited to:
class SocketHandler(WebSocketHandler):
def on_message(self, message):
ball = json.loads(message)
user = User(ball['identifier'])
if ball['command'] == 'search':
global our_mongo
search_results = our_mongo.find({'$text':{'$search':ball['search_term']}},{'score':{'$meta':"textScore"}})
self.write_message({
'command': 'search-results',
'results': list(search_results.sort([('score', {'$meta': 'textScore'})]).limit(10)),
})
elif ball['command'] == 'save-node': # hopefully this can handle both new nodes and changes to nodes
node_dict = ball['node_dict']
if 'importance' in node_dict.keys():
node_dict['importance'] = int(node_dict['importance'])
try:
node_obj = node.create_appropriate_node(node_dict)
print('node made. looks like: '+str(node_obj)+'. Now time to put it into the DB...')
global our_mongo
# take a look at the dependencies now
previous_dependency_ids = [node.reduce_string(dependency) for dependency in list(our_mongo.find({"_id": node_obj.id}))[0]["_dependencies"]] # if this works, try using set() instead of list and elimination the set()s below
print('prev deps are: '+str(previous_dependency_ids))
our_mongo.upsert({ "_id": node_obj.id }, node_obj.__dict__)
# take a look at the current dependencies
current_dependency_ids = [node.reduce_string(dependency) for dependency in list(our_mongo.find({"_id": node_obj.id}))[0]["_dependencies"]] # if this works, try using set() instead of list and elimination the set()s below
print('curr deps are: '+str(current_dependency_ids))
update_our_DAG()
# send an update of the graph to the user if there is a new dependency:
for new_dependency in set(current_dependency_ids) - set(previous_dependency_ids):
self.request_node(new_dependency, ball, user)
# OR IF THE SAVED NODE IS A BRAND NEW NODE, we have to include all the deps
except Exception as error:
# stuff didn't work, send error back to user
print('ERROR: '+str(error))
self.write_message({
'command': 'display-error',
'message': str(error),
})
def update_our_DAG():
# 1. grab nodes and edges from database
all_node_dicts = list(Mongo("math", "nodes").find())
# 2. create a networkx graph with the info...
global our_DAG
our_DAG = nx.DAG()
for node_dict in all_node_dicts:
node = create_appropriate_node(strip_underscores(node_dict))
our_DAG.add_n(node)
def make_app():
return Application(
[
url('/', RedirectHandler, {"url": "index.html"}, name="rooth"),
url('/websocket', SocketHandler),
url('/json', JSONHandler, name="jsonh"),
url(r'/index(?:\.html)?', IndexHandler, name="indexh"),
# captures anything at all, and serves it as a static file. simple!
url(r'/(.*)', StaticHandler, {"path": "../www/"}),
],
# settings:
debug=True,
)
def make_app_and_start_listening():
# enable_pretty_logging()
application = make_app()
# by listening on the http port (default for all browsers that i know of),
# user will not have to type "http://" or ":80" in the URL
application.listen(80)
# other stuff
IOLoop.current().start()
if __name__ == "__main__":
# 0. create a global mongo object for later use (for upserts in the future)
our_mongo = Mongo("math", "nodes")
# 1 and 2
update_our_DAG()
axioms = [our_DAG.n(node_id) for node_id in ['set', 'multiset', 'vertex']]
# 3. launch!
make_app_and_start_listening()
I've tried everything and it's still an issue. WHY is this a syntax error? What is the best way to fix this?
It is not a SyntaxError but a SyntaxWarning. Python warns that you're using our_mongo variable before a global our_mongo within the function.
Syntactically it does not actually matter on which line of the function the global statement is; but the idiomatic way would be to use global before the first access.
Another issue altogether is that you have multiple global our_mongo statements, where single would do, and also that you do not even need the global at all - it is only for the case when you want to assign a new value to the global variable; i.e.
def foo():
global bar # this line is required for the next line
# to change the global variable instead of
# the local variable
bar = 42
Thus, just remove all global statements altogether from your on_message, they're unnecessary.
The only reason a name is declared global in a function is so that it will be bound in global scope instead of local scope; when accessing a name the global scope is always searched regardless. Since your code never binds our_mongo within any function there is no reason to declare it global in the first place.

do i have to declare a variable twice when it is already declared in another def?

I am making a arithmetic quiz in python. At the start of the quiz it asks the user what class they would like to input results for, this is in the def classname(). Later on when the quiz is finished, the program will write the scores to a text file if the user does not want to repeat the quiz like this:
def classname():
class_name = input("Which class do you wish to input results for?")
# the rest of my code for my introduction
#
#
#
#
def askquestion():
# all of my code to ask the user the arithmetic question
#
#
#
# code to ask the user if they want to repeat the quiz
#if they dont want to repeat the quiz then this code is run
else:
filename = class_name + ".txt"
# code that will write the scores to a text file
When i run this code i get this error:
filename = class_name + ".txt"
NameError: name 'class_name' is not defined
Do I have to declare the variable "classname" again in askquestion() or is there a way python can recognise I have already declared the variable?
Unless you define your variable as global, you will have to redefine it, or pass the value in your code to subsequent functions as an argument.
You can pass in an argument to askquestion, as it currently stands, the variable class_name is out of scope for the function .
So, your function definition changes to
def askquestion(class_name):
...
...
And now, when you call the askquestion function, you will have to pass the class_name to it.
A working example will look something like below:
def classname():
class_name = input("Which class do you wish to input results for?")
...
...
return class_name
def askquestion(class_name):
...
else:
filename = class_name + ".txt"
# code that will write the scores to a text file
if __name__ == `__main__`:
class_name = classname()
askquestion(class_name)
Variables declared inside a function are local to that function and either need to be passed or returned to other methods or moved outside of the function to make it global, which needs to be explicitly declared when using it inside a function:
So you could return the class_name from classname() and use classname() in askquestion():
def classname():
class_name = input("Which class do you wish to input results for?")
return class_name
def askquestion():
...
else:
filename = classname() + ".txt"
# code that will write the scores to a text file
Your code declares the variable class_name inside the class_name() function, so it's not accessible outside. If you declare the variable class_name outside the class_name() function, it will be accessible to the askquestion() function.

Categories

Resources