SQLite SELECT Abfrage / PC=True / Android=False

Installation und Anwendung von Datenbankschnittstellen wie SQLite, PostgreSQL, MariaDB/MySQL, der DB-API 2.0 und sonstigen Datenbanksystemen.
Antworten
Msi.
User
Beiträge: 4
Registriert: Samstag 1. April 2023, 16:21

Hallo,
da ich neu bin, winke ich erst mal in die Runde. :D
 
Ich habe eine SQLite-Datenbank auf dem PC, mit der ich aus meinem Programm heraus auch erfolgreich interagiere.
Nun habe ich das Programm und die DB auf ein Android-System installiert und sobald es dort startet und der Codeteil unten ausgeführt wird, bricht es ab.
 
Den Pfad der DB überprüfe ich vorher mit os.path.isfile(strPath). Der sollte stimmen.
 
(Ich nutze Buildozer, um die .apk Datei zu erstellen. Es werden zusätzlich Kivy 2.1.0 und Kivymd 1.1.1 für die GUI genutzt. Auf einem Android 10)
 
Nun frage ich mich:
- Muss ich im Android spezielle Freigaben geben, damit aus dem Programm heraus auf die Datei zugegriffen werden darf?
- usw

Naja, ich würde mich freuen, wenn sich jemand findet, der sich damit auskennt und mir ein paar Stichpunkte geben kann, um weiter zu recherchieren oder sogar gleich weiß, woran es liegt.

Code: Alles auswählen

import sqlite3


def getAllRezept_Name(strPath):
	try:
		db = sqlite3.connect(strPath)	
		c = db.cursor()
		cmd = """SELECT Name FROM Tbl_Rezepte;"""	
		c.execute(cmd)
		return c.fetchall()
	except Error as e:
		return ("ERR:","|MDL: dbCtr |FU: getAllRezept_Name |Error Text: ", e)
	finally:
		c.close()
		db.close()

Schöne Grüße
Benutzeravatar
sparrow
User
Beiträge: 4195
Registriert: Freitag 17. April 2009, 10:28

Namen schreibt man in Python per Konvention klein_mit_unterstrich. Und Denglish sollte man auch vermeiden.
Also get_all_recipe_names statt getAllRezept_Name.
strPath ist doppelt verwirrend. Ist das nicht eher der db_path?
Und wohin führt der? Denn du darfst sicher nicht überall schreiben. Und du kannst nicht davon ausgehen, dass das Arbeitsverzeichnis das ist, von dem du denkst, dass es ist.

Soweit ich mich erinnere funktioniert es, wenn man die Datenbank im selben Verzeichnis wie das Script ablet. Den Pfad musst du aber über die Variable __file__ selbst ermitteln. Also in etwa:

Code: Alles auswählen

from pathlib import Path

app_path = Path(__file__).parent
db_path = app_path / "database.sqlite"
Msi.
User
Beiträge: 4
Registriert: Samstag 1. April 2023, 16:21

Danke für die schnelle Antwort.

Das Denglisch ist mir jetzt auch aufgefallen, nach 5 mal lesen... :roll:
strPath steht für „StringPath". Das ist so bei mir drin. Ich muss mich noch an die Python-Konvention gewöhnen.

Ich weiß noch nicht, ob ich heute zum Weitermachen komme, aber ich werde berichten.
Schöne Grüße
Msi.
User
Beiträge: 4
Registriert: Samstag 1. April 2023, 16:21

Moin,

ich dachte, ich schreib hier nochmal, damit du weißt, dass deine Antwort nicht im Leeren verlaufen ist.
Allerdings haben die aktuellen Fragen nichts mehr mit Datenbanken zu tun.

Mit deiner Abfrage funktionierte es ganz gut, gefunden habe ich auch die folgende mit dem es ebenfalls funktioniert.

Code: Alles auswählen

package_dir = os.path.abspath(os.path.dirname(__file__))
db_dir = os.path.join(package_dir,'rzp.db')
Nur, wenn ich es richtig herausgefunden habe, wird nach beiden Abfragen die DB im „Internal File Storage" angelegt oder im „Database" da bin ich mir nicht sicher. Den „Internal File Storage" hat jedes Programm im Android für sich selber, der ist nach außen gesperrt. Die DB müsste in den „External File Storage" um sie so zu nutzen wie ich es plane. Wie das geht weiß ich noch nicht.

Falls jemand mit Python ebenfalls auf den External File Storage im Android zugreifen möchte und Buildozer zum kompilieren nutzt, müsste bei Buildozer die Premission einstellen:

Code: Alles auswählen

android.premission = WRITE_EXTERNAL STORAGE, READ_EXTERNAL_STORAGE
Ebenso bei Python:

Code: Alles auswählen

from kivy import platform

if platform != "linux":
	from android.permissions import request_permissions, Permission
	request_permissions([Permission.READ_EXTERNAL_STORAGE, Permission.WRITE_EXTERNAL_STORAGE])
Schöne Grüße
Benutzeravatar
__blackjack__
User
Beiträge: 13115
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Msi.: Die `close()`-Aufrufe im ``finally`` sind beide potentiell fehlerhaft, weil weder `c` noch `db` garantiert definiert sind wenn dieser Block ausgeführt wird.

Im Normalfall eine Sequenz mit Ergebnissen zurückgeben und im Fehlerfall eine Sequenz mit zwei Texten und der Ausnahme ist eine kaputte API. Die Informationen in den beiden Texten stecken zudem im Traceback zur Ausnahme. Man sollte in Fehlertexte nicht manuell noch mal den Modul und den Funktionsnamen schreiben. Das ist äusserst fehleranfällig, macht unnötig Arbeit, und macht noch mal mehr Arbeit wenn man das nicht überall aktuell hält und im Fehlerfall dann dort falsche Ortsangaben zu finden sind. Die kaputte Ausnahmebehandlung gehört dort so nicht in die Funktion. Zudem `Error` undefiniert ist.

Das die Rezeptnamen noch mal in Tupeln oder etwas ähnlichem verpackt sind, ist sicher auch überraschend für den Aufrufer.

Beim Tabellennamen würde ich weder das komischen Kürzel `tbl` davor packen noch das in Mehrzahl benennen. Ein Tabellenname, also ein Entity beziehungs eine Relation, ist im Grunde so etwas wie eine Klasse. Eine Klasse die *ein* Rezept beschreibt heisst ja auch nicht `Rezepte` sondern `Rezept`.

Ungetestet:

Code: Alles auswählen

import sqlite3
from contextlib import closing


def get_all_recipe_names(db_path):
    with closing(sqlite3.connect(db_path)) as db:
        with closing(db.cursor()) as cursor:
            cursor.execute("SELECT name FROM rezept")
            return [row[0] for row in cursor.fetchall()]
Auch wenn die Rezeptnamen eindeutig sind, würde man besser noch mit einer künstlichen ID arbeiten. Sonst müsste man den Namen als Fremdschlüssel verwenden, was schon mal ineffizient wäre, und das umbenennen von Rezepten mehr oder weniger unmöglich macht.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Antworten