[django] Klasse wird nicht mehr gefunden, wenn in funktion

Django, Flask, Bottle, WSGI, CGI…
Antworten
frank-w
User
Beiträge: 21
Registriert: Freitag 19. Februar 2021, 18:38

Hallo,

ich bin gerade dabei mich in ein Django-Projekt einzuarbeiten, welches von einem Kollegen gebaut wurde.

Hier sind Datenbank-modelle definiert welche sowohl in einer webapp (Rest-api) verwendet werden. diese möchte ich nun in Standalone-scripten verwenden.

also habe ich mir ein script geschrieben, welches diese importiert und nutzt (stark gekürzt, daher keine Ausgabe o.ä. Zeilennummern kmmen durch auskommentierten code, welchen ich hier rausgeworfen habe):

jobevaluation.py:

Code: Alles auswählen

from api.models import Job #in api/models.py definiert

jobs=Job.objects.filter(jobtype__jobtype="checktrails",state__state="EXECUTIONREQUESTED")
ich starte das über ein shell-script, damit das environment auch geladen wird

scr.sh:

Code: Alles auswählen

#!/bin/bash
source $HOME/django/env3.8/bin/activate
export PYTHONPATH=$HOME/django/env3.8/lib/python3.8/site-packages/

if [[ -e "$1" ]];then
        python3 manage.py shell < $1
fi
So funktioniert es :)

Nun möchte ich den wenigen Code einfach nur in eine Funktion packen:

Code: Alles auswählen

def CheckOpenJobs():
    jobs=Job.objects.filter(jobtype__jobtype="checktrails",state__state="EXECUTIONREQUESTED"

CheckOpenJobs()
nun findet er aber plötzlich das DB-Model nicht mehr

Code: Alles auswählen

$ ./scr.sh jobevaluation.py
Traceback (most recent call last):
  File "manage.py", line 60, in <module>
    execute_from_command_line(sys.argv)
  File "/home/A749523/django/env3.8/lib/python3.8/site-packages/django/core/management/__init__.py", line 371, in execute_from_command_line
    utility.execute()
  File "/home/A749523/django/env3.8/lib/python3.8/site-packages/django/core/management/__init__.py", line 365, in execute
    self.fetch_command(subcommand).run_from_argv(self.argv)
  File "/home/A749523/django/env3.8/lib/python3.8/site-packages/django/core/management/base.py", line 288, in run_from_argv
    self.execute(*args, **cmd_options)
  File "/home/A749523/django/env3.8/lib/python3.8/site-packages/django/core/management/base.py", line 335, in execute
    output = self.handle(*args, **options)
  File "/home/A749523/django/env3.8/lib/python3.8/site-packages/django/core/management/commands/shell.py", line 92, in handle
    exec(sys.stdin.read())
  File "<string>", line 30, in <module>
  File "<string>", line 21, in CheckOpenJobs
NameError: name 'Job' is not defined
das kann ich mir nicht erklären...

importiere ich nun dieses script in eine jobtest.py (funktionsaufruf in die jobtest verschoben) und starte das, funktioniert es wieder

Code: Alles auswählen

from jobevaluation import *

CheckOpenJobs()
./scr.sh jobtest.py

kann mir das jemand erklären?

Alle Dateien (jobevaluation.py,jobtest.py und scr.sh) liegen im Hauptverzeichnis des Django-Projekts

Gruß Frank
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

Wenn er das nicht findet, fehlt der Import. Bitte die gesamten Quellen der beteiligten Skripte zeigen.
frank-w
User
Beiträge: 21
Registriert: Freitag 19. Februar 2021, 18:38

Der import fehlt nicht...habe nur die Änderung gepostet...

So funktioniert es:

Code: Alles auswählen

from api.models import Job #in api/models.py definiert

jobs=Job.objects.filter(jobtype__jobtype="checktrails",state__state="EXECUTIONREQUESTED")
So nicht:

Code: Alles auswählen

from api.models import Job #in api/models.py definiert

def CheckOpenJobs():
    jobs=Job.objects.filter(jobtype__jobtype="checktrails",state__state="EXECUTIONREQUESTED"

CheckOpenJobs()
Benutzeravatar
__blackjack__
User
Beiträge: 13182
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@frank-w: Das kann nicht sein. Da gibt es einen Syntaxfehler. Und wenn man den behebt, dann kann es den beschriebenen Fehler nicht geben, weil `Job` offensichtlich importiert wird, und damit definiert ist.

Zeig den tatsächlichen Code.
“There will always be things we wish to say in our programs that in all known languages can only be said poorly.” — Alan J. Perlis
frank-w
User
Beiträge: 21
Registriert: Freitag 19. Februar 2021, 18:38

mit dem import-workaround habe ich aber bereits weiterentwickelt...habe daher die dateien kopiert (nur anderer name+import in jobtest angepasst) und auf das minimum angepasst:

jobevaluation_2.py:

Code: Alles auswählen

from api.models import Job

def CheckOpenJobs():
    resultdata={}
    jobs=Job.objects.filter(jobtype__jobtype="checktrails",state__state="EXECUTIONREQUESTED")
    return resultdata

if __name__ == "django.core.management.commands.shell":
    CheckOpenJobs()

jobtest_2.py:

Code: Alles auswählen

from jobevaluation_2 import *
import json

data=CheckOpenJobs()

print(json.dumps(data, indent=2))
das ist wirklich der komplette inhalt beider dateien zum zeitpunkt des tests, dessen Ausgabe jetzt kommt (wurden auch ohne Änderungen hintereinander gemacht):

Code: Alles auswählen

$ ./scr.sh jobtest_2.py
{}
$ ./scr.sh jobevaluation_2.py
Traceback (most recent call last):
  File "manage.py", line 60, in <module>
    execute_from_command_line(sys.argv)
  File "/home/A749523/django/env3.8/lib/python3.8/site-packages/django/core/management/__init__.py", line 371, in execute_from_command_line
    utility.execute()
  File "/home/A749523/django/env3.8/lib/python3.8/site-packages/django/core/management/__init__.py", line 365, in execute
    self.fetch_command(subcommand).run_from_argv(self.argv)
  File "/home/A749523/django/env3.8/lib/python3.8/site-packages/django/core/management/base.py", line 288, in run_from_argv
    self.execute(*args, **cmd_options)
  File "/home/A749523/django/env3.8/lib/python3.8/site-packages/django/core/management/base.py", line 335, in execute
    output = self.handle(*args, **options)
  File "/home/A749523/django/env3.8/lib/python3.8/site-packages/django/core/management/commands/shell.py", line 92, in handle
    exec(sys.stdin.read())
  File "<string>", line 9, in <module>
  File "<string>", line 5, in CheckOpenJobs
NameError: name 'Job' is not defined
in der api/models.py wären die Klassen so definiert:

Code: Alles auswählen

class Jobtype(models.Model):
    id = models.AutoField(primary_key=True)
    jobtype         = models.CharField(max_length=100, blank=True, null=True,db_column="name")
    description     = models.CharField(max_length=50)
    filter          = models.CharField(max_length=50, blank=True, null=True)
    kbez            = models.CharField(max_length=100, blank=True, null=True)

    class Meta:
        managed = False
        db_table = 'v_jobtype'


class Jobstate(models.Model):
    id = models.AutoField(primary_key=True)
    state = models.CharField(max_length=100, blank=True, null=True,db_column="name")
    description = models.CharField(max_length=50)
    filter = models.CharField(max_length=50, blank=True, null=True)
    kbez = models.CharField(max_length=100, blank=True, null=True)

    class Meta:
        managed = False
        db_table = 'v_jobstate'


class Job(models.Model):
    id = models.AutoField(primary_key=True)

    head = models.ForeignKey(Head,on_delete=CASCADE,null=True)
    jobtype = models.ForeignKey(Jobtype,on_delete=CASCADE,null=True,db_column="apptype_jobtype_id")
    data = models.TextField()
    starttime = models.DateTimeField(blank=True, null=True)
    endtime = models.DateTimeField(blank=True, null=True)
    state = models.ForeignKey(Jobstate,on_delete=CASCADE,null=True,db_column="apptype_jobstate_id")

    class Meta:
        managed = False
        db_table = 'jobs'
ich vermute django macht irgendwas mit der MRO (oder wie das bei Objekten/Klassen heist), weshalb es in funktionen nur funktioniert, wenn die Datei "imported"´wurde. Sonst macht es für mich auch keinen Sinn
Benutzeravatar
sparrow
User
Beiträge: 4226
Registriert: Freitag 17. April 2009, 10:28

Ich würde erst einmal ausschließen, dass es an der seltsamen Art des Aufrufs liegt und das Script per Hand ausführen.
Und anstatt Dinge in die Django-Shell zu kippen, würde ich Django in dem Script initialisieren und dann damit arbeiten. Das sieht mir nicht besonders konsistent und auch nicht plattformübergreifend aus.
frank-w
User
Beiträge: 21
Registriert: Freitag 19. Februar 2021, 18:38

wie kann ich das script direkt aufrufen und das DB-Model verwenden?

kenne nur den Weg über die django-shell
Benutzeravatar
sparrow
User
Beiträge: 4226
Registriert: Freitag 17. April 2009, 10:28

Im ersten Schritt in der Django Shell:

Code: Alles auswählen

import jobevaluation_2
jobevaluation_2.CheckOpenJobs()
Und CheckOpenJobs sollte natürlich eigentlich check_open_jobs heißen. Ist ja keine Klasse.
frank-w
User
Beiträge: 21
Registriert: Freitag 19. Februar 2021, 18:38

so funktioniert es, aber hier ist ja auch wieder der import drin...

Code: Alles auswählen

$ source $HOME/django/env3.8/bin/activate
$ export PYTHONPATH=$HOME/django/env3.8/lib/python3.8/site-packages/
$ python3 manage.py shell
Python 3.8.0 (default, Feb 25 2021, 22:10:10)
[GCC 8.4.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>> import jobevaluation_2
>>> jobevaluation_2.check_open_jobs()
{}
>>>
ich habe jetzt auch mal versucht es ohne import aufzurufen...musste dafür nur den importguard anpassen und ein print reinmachen

Code: Alles auswählen

print(__name__)
if __name__ == "django.core.management.commands.shell" or __name__ == "builtins":
    print(check_open_jobs())

Code: Alles auswählen

>>> exec(open('jobevaluation_2.py').read())
builtins
{}
das funktioniert also auch...also irgendwas ist mit dem pipe richtung python-shell faul...nur warum schlägt sich das erst nieder, wenn man den Objektzugriff in eine Funktion packt...
Benutzeravatar
__blackjack__
User
Beiträge: 13182
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@frank-w: Da wird etwas anderes ausgeführt als das was Du glaubst. Denn wenn oben im Modul ein ``import`` von `Job` steht, und der *funktioniert*, dann ist der Name auch in der Funktion bekannt.

Ansonsten wäre es mal interessant ein minimales, lauffähiges Beispiel zu sehen, das dieses Problem hat.
“There will always be things we wish to say in our programs that in all known languages can only be said poorly.” — Alan J. Perlis
frank-w
User
Beiträge: 21
Registriert: Freitag 19. Februar 2021, 18:38

__blackjack__ hat geschrieben: Donnerstag 2. September 2021, 13:44 @frank-w: Da wird etwas anderes ausgeführt als das was Du glaubst. Denn wenn oben im Modul ein ``import`` von `Job` steht, und der *funktioniert*, dann ist der Name auch in der Funktion bekannt.

Ansonsten wäre es mal interessant ein minimales, lauffähiges Beispiel zu sehen, das dieses Problem hat.
ja, so sollte es sein....aber irgendwas daran wird mit der übergabe an die python shell zerschossen...das beispiel oben (4 zeilen...import,funktions-kopf,job-zugriff und funktionsaufruf) ist doch so klein wie möglich ;) kleiner gehts doch nicht mehr. man braucht nur ein lauffähiges Django und eine Datenbank, damit man sich das Model so mit übernehmen kann. ich nehme abe an, man kann ein beliebiges Model nehmen, habe die Definition nur gepostet, damit man das nachstellen könnte. den Filter kann man rausmachen...das knallt trotzdem, da er ja Job nicht findet
Benutzeravatar
__blackjack__
User
Beiträge: 13182
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@frank-w: Es ist zu klein. Ich meinte damit was was man wirklich einfach so ausprobieren kann, ohne noch irgendwas selbst schreiben zu müssen oder raten zu müssen wo man was noch ergänzen muss.

Wobei das mit der Shell halt, wie ja auch schon von anderer Seite geschrieben wurde, sowieso ein bisschen komisch ist. Wenn ich ein Programm schreiben wollte, würde ich entweder ein manage-Command schreiben, oder eben den notwendigen Tanz am Anfang des Programms durchführen um die Django-Umgebung mit den gewünschten Einstellungen zu initialisieren.
“There will always be things we wish to say in our programs that in all known languages can only be said poorly.” — Alan J. Perlis
frank-w
User
Beiträge: 21
Registriert: Freitag 19. Februar 2021, 18:38

__blackjack__ hat geschrieben: Donnerstag 2. September 2021, 14:33 @frank-w: Es ist zu klein. Ich meinte damit was was man wirklich einfach so ausprobieren kann, ohne noch irgendwas selbst schreiben zu müssen oder raten zu müssen wo man was noch ergänzen muss.
eigentlich nur die 3 Dateien scr.sh (ggf. env anpassen), jobtest.py und jobevaluation.py mit dem geposteten Inhalt erstellen...statt dem Job-Objekt funktioniert sicher auch ein anderes evtl. vorhandenes aus der api/models.py (ist ja von django selbst und wird nur erweitert je nach DB). dann muss natürlich die jobevaluation.py angepasst werden (import+Objekt-Zugriff)
__blackjack__ hat geschrieben: Donnerstag 2. September 2021, 14:33 Wobei das mit der Shell halt, wie ja auch schon von anderer Seite geschrieben wurde, sowieso ein bisschen komisch ist. Wenn ich ein Programm schreiben wollte, würde ich entweder ein manage-Command schreiben, oder eben den notwendigen Tanz am Anfang des Programms durchführen um die Django-Umgebung mit den gewünschten Einstellungen zu initialisieren.
wie würde so ein manage-command aussehen?

ich kenne aktuell nur den Weg, den mir der Kollege gegeben hat mit der scr.sh oder das ganze muss als webservice laufen, was hier aber nicht gewünscht ist, da es sich um backend-scripte handelt und nicht für Web-Zugriff.
Benutzeravatar
sparrow
User
Beiträge: 4226
Registriert: Freitag 17. April 2009, 10:28

Ich bin da __blackjack__s Meinung. Entweder ein manage-Command oder du suchst dir heraus, wie man die Django-Umgebung in einem Script initialisiert.
Aber den Weg, den dein Kollege dir da gezeigt hat, ist ungewöhnlich und offensichtlich kaputt.
frank-w
User
Beiträge: 21
Registriert: Freitag 19. Februar 2021, 18:38

danke euch erstmal...ich versuche morgen mal das hier umzusetzen:

https://django-extensions.readthedocs.i ... cript.html

will nur das Initialisieren des environments irgendwie mit vor dem manage.py automatisieren
frank-w
User
Beiträge: 21
Registriert: Freitag 19. Februar 2021, 18:38

Habe es jetzt mit dem runscript (und einem alias für das hochziehen des env) erfolgreich umsetzen können
Antworten