Seite 1 von 1
Wie einen Einzelwert in bestehende Zeile einfügen?
Verfasst: Dienstag 23. Dezember 2008, 15:03
von arien101
Hallo,
vorneweg: Ich bin totaler Anfänger was programmieren und auch was Datenbanken angeht. Da ich durch die Wirtschaftskrise gerade viel Zeit habe, wollte ich mit beides beibringen. Dazu fiel meine Wahl auf Pyhton, da es hier viele verständliche Tutorials gab.
Zur Übung bastle ich mir ein kleines Pseudo-Rollenspiel. Die Charakter-Generierung habe ich durch und will die Ergebnisse jetzt in eine Datenbank speichern. Etwa so:
player_id | login | pass | chr_name | chr_job |
player_1 | meinlogin | passwort | John | Doe |
player_2 | deinlogin | wortpass | Conan | der Barbar |
Nur mit dem Unterschied, dass zuerst an einer Stelle login und pass abgefordert und gespeichert werden und dann später an einer anderen Stelle im Programm chr_name und chr_job.
Sobald ich login und pass habe, schreibe ich momentan folgendes in die Datenbank:
Code: Alles auswählen
data_inserter = {
"player_id" : player_id, "login_name" : login_name, "login_password" : login_password
}
sql_inserter = "INSERT INTO players VALUES (:player_id, :login_name , :login_password, null, null)"
cursor.execute(sql_inserter, data_inserter)
Damit möchte ich erreichen, dass die beiden letzten Spalten chr_name und chr_job unbelegt bleiben.
Jetzt möchte ich einem zweiten Schritt etwas nur in diese letzten beiden Spalten der Zeile schreiben. Wie mache ich das?
Bin für jeden Tip dankbar!
Verfasst: Dienstag 23. Dezember 2008, 15:30
von Birne94
Code: Alles auswählen
update players set chr_name=WERT, chr_jop=WERT2 where player_id=PLAYERID
Verfasst: Dienstag 23. Dezember 2008, 15:37
von arien101
Super, das hat geklappt.
Dankeschön

Verfasst: Dienstag 23. Dezember 2008, 15:39
von Leonidas
Hint: Unter Python nutzt man öfter ORMs als SQL direkt. Mit Klassen und Objekten arbeiten ist nun doch etwas angenehmer als mit Strings.
Verfasst: Dienstag 23. Dezember 2008, 15:47
von arien101
Hi Leonidas,
ja, ich hatte vorher das Forum durchsucht und deine Hinweise auf SQLALchemy entdeckt. Da ich aber bisher 0 Ahnung von SQL habe, wollte ich erstmal mit SQL rumprobieren.
Wenn ich das halbwegs verstanden habe, würde ich dann mit einem ORM weitermachen.
Bisher bin ich noch damit beschäftigt mich durch alle möglichen Tutorials durchzubeißen. Ich bin eigentlich BWLer und tu mich mit Programmieren & Logik deshalb etwas schwer

Verfasst: Dienstag 23. Dezember 2008, 16:40
von sma
Bei Rollenspiel werde ich hellhörig, allerdings scheint mir das als Beispiel für eine Datenbank-zentrische Anwendung recht weit hergeholt -- oder aber ich habe eine komplett andere Vorstellung davon, was das sein soll. Ich würde da an
Ultima,
The Bard's Tale oder
Gothic denken.
Letztlich simuliert ein Computer-Rollenspiel ja Objekte in einer virtuellen Welt und dafür wäre objektorientierte Programmierung, wie sie mit Python möglich ist, durchaus geeignet.
Relationale Datenbanken (mysql und so) und objektorientierte Programmierung passen allerdings nicht optimal zusammen. Ein sogenannter objektrelationaler Mapper (ORM), kann helfen, den Schmerz zu verringern, doch den Stein der Weisen hat da noch niemand gefunden. Objektorientierte Datenbanken sind hingegen wenig verbreitet.
Ganz pur "bare bone" SQL benutzen würde ich allerdings nur, wenn ich's lernen will ;) Andernfalls wird das ganz schnell so umständlich und fehleranfällig, dass man sich da mit einem ORM helfen lassen will. Da hättest du dann vielleicht etwas wie
Code: Alles auswählen
# Django
player = Player.objects.create(name="Conan", job="König von Aquilonien")
player.save()
--oder--
# SQLalchemy
session = Session()
session.add(Player("Conan", "König von Aquilonien"))
session.commit()
arien101, ich glaube, du suchst die SQL-Anweisung UPDATE, bin mir aber nicht sicher, ob ich wirklich verstehe, was du willst. Ein ORM wie SQLalchemy kümmert sich darum, automatisch den richtigen SQL-Befehl zu verwenden, je nachdem was und wie du es änderst.
Ich würde aber empfehlen, erstmal nur mit Python zu starten. Dort kann man ein Netz von Objekten, wie sie bei einer virtuellen Welt üblicherweise entstehen, dann mit "pickle" auf einen Schlag speichern und später wieder laden. Dann musst du dir um das einzelne Speichern der Daten überhaupt keine Gedanken machen und kannst dich erst mal auf den kreativen Prozess der Programmierung des Spiel konzentrieren.
Stefan
Re: Wie einen Einzelwert in bestehende Zeile einfügen?
Verfasst: Dienstag 23. Dezember 2008, 17:08
von DasIch
Etwas OT:
arien101 hat geschrieben:Sobald ich login und pass habe, schreibe ich momentan folgendes in die Datenbank:[...]
Wenn du wirklich dass Passwort selbst in die Datenbank schreiben willst ist das eine schlechte Idee. Keiner Nutzer möchte gerne sein Passwort ungeschützt jemand anders geben. Du solltest den Hash speichern, wobei du vorher dass Passwort mit mit "salt" verändern solltest. Etwa so:
Code: Alles auswählen
from string import digits, ascii_letters, punctuation
import hmac
import random
def create_salt(chars=digits+ascii_letters+punctuation, length=8):
if length <= 0:
raise ValueError('Can\'t create a str with less than 1 character')
return ''.join(random.choice(chars) for _ in xrange(length))
def create_password_hash(password, salt=None):
salt = salt or create_salt()
salt = salt.replace('$', '')
h = hmac.new(password, salt).hexdigest()
return 'sha1${salt}${hash}'.format(salt=salt, hash=h)
def check_password_hash(password, password_hash):
method, salt, h = password_hash.split('$')
return h == hmac.new(password, salt).hexdigest()
Wie du es schaffst die Hashmethode optional zu wählen sei dir an dieser Stelle selbst überlassen.
[/ot]
Ich würde dir übrigens wie meine Vorgänger zu einem ORM wie SQLAlchemy raten, damit würdest du ein Model erstellen dass dann z.B. so aussieht.
Code: Alles auswählen
class Player(Model):
__tablename__ = 'players'
id = Column(Integer, primary_key=True, autoincrement=True)
username = Column(String(30))
password_hash = Column(String(150))
char_name = Column(String(30))
char_job = Column(String(50))
def __init__(self, username, password, char_name, char_job):
self.username = username
self.pasword_hash = create_password_hash(password)
self.char_name = char_name
self.char_job = char_job
Beim Login würdest du nach dem `username` in der Datenbank suchen, das Password überprüfen und dann das Player Objekt z.B. an ein Anwendungsobjekt binden.
Code: Alles auswählen
player = Player.query.filter_by(username=login_name).first()
if player and check_password_hash(login_password, player.password_hash):
local.application.current_player = player
Du musst dir keine Sorgen ums Escapen machen, du musst dich nicht mit einer zweiten Sprache(SQL) herumplagen, du bist unabhängig von der Datenbankanbindung, dass ganze ist etwas "natürlicher" in der Verwendung und dein Code nicht vollgemüllt mit irgendwelchen Strings in denen SQL Anweisungen stehen.
P.S.:Ja, ich hatte etwas langeweile

Verfasst: Dienstag 23. Dezember 2008, 17:29
von derdon
@DasIch: Anstatt das Dollarzeichen später zu entfernen, kannst du die create_salt-Funktion auch gleich etwas umschreiben, damit man nicht erwünschte Zeichen angeben kann:
Code: Alles auswählen
#!/usr/bin/env python
import random
from string import letters, digits
def generate_salt(length=8, include=letters + digits, exclude="$"):
chars = list(set(include) ^ set(exclude))
return "".join(random.sample(chars, length))
Verfasst: Mittwoch 24. Dezember 2008, 17:21
von arien101
Hey,
danke für die vielen Antworten! Meine konkrete Frage war mit dem zweiten Post beantwortet. Ich suchte tatsächlich die SQL-Anweisung UPDATE. Damit klappt es jetzt auch.
sma schrieb:
Bei Rollenspiel werde ich hellhörig, allerdings scheint mir das als Beispiel für eine Datenbank-zentrische Anwendung recht weit hergeholt -- oder aber ich habe eine komplett andere Vorstellung davon, was das sein soll. Ich würde da an Ultima, The Bard's Tale oder Gothic denken.
Ja, so was ähnliches ist tatsächlich gemeint, aber es ist wirklich nur als Übungsaufgabe für mich gedacht. In dem Buch dessen Beispiele ich durcharbeite geht es um eine Bank, für die dann eine Konto-Klasse aufgebaut wird mit Methoden wie Einzahlung, Auszahlung usw. Das war mir zu trocken, deshalb versuche ich die ganzen Beispiele für mich so umzubasteln, dass sie auf ein Rollenspiel passen.
Der Grund warum ich SQL im Beispiel durcharbeite hat also weniger mit dem Rollenspiel zu tun, sondern mehr damit dass das Thema SQL halt im Tutorial vorkommt. Zum Thema "pickle" bin ich noch nicht durchgedrungen. Das hab ich nur überflogen.
@DasIch:
Danke für die Hinweise. Tatsächlich hatte ich mir schon überlegt wie ich mit dem Passwort umgehe, hatte das aber auf später verschoben.
Ingsgesamt bin ich von eurer Hilfsbereitschaft echt umgehauen. Ich werde mal versuchen das Feedback einzubauen und das Programm soweit zu schreiben, das es das macht was ich will:
Benutzerdaten für Charaktergenerierung abfragen und diese Infos in einer Datenbank speichern.
Sobald ich das geschafft habe poste ich das Ergebnis dann hier und dann habt ihr was zu lachen
PS: Frohe Weihnachten
Verfasst: Samstag 27. Dezember 2008, 21:38
von arien101
Hallo,
das Programm macht jetzt so ziemlich das was ich wollte. Daraufhin habe ich mir die Anmerkungen zur Passwortsicherheit von DasIch vorgenommen:
DasIch hat geschrieben:Etwas OT:
arien101 hat geschrieben:Sobald ich login und pass habe, schreibe ich momentan folgendes in die Datenbank:[...]
Wenn du wirklich dass Passwort selbst in die Datenbank schreiben willst ist das eine schlechte Idee. Keiner Nutzer möchte gerne sein Passwort ungeschützt jemand anders geben. Du solltest den Hash speichern, wobei du vorher dass Passwort mit mit "salt" verändern solltest. Etwa so:
Code: Alles auswählen
from string import digits, ascii_letters, punctuation
import hmac
import random
def create_salt(chars=digits+ascii_letters+punctuation, length=8):
if length <= 0:
raise ValueError('Can\'t create a str with less than 1 character')
return ''.join(random.choice(chars) for _ in xrange(length))
def create_password_hash(password, salt=None):
salt = salt or create_salt()
salt = salt.replace('$', '')
h = hmac.new(password, salt).hexdigest()
return 'sha1${salt}${hash}'.format(salt=salt, hash=h)
def check_password_hash(password, password_hash):
method, salt, h = password_hash.split('$')
return h == hmac.new(password, salt).hexdigest()
Den Code habe ich mittlerweile verstanden und weiß jetzt auch wozu Hashes und Salts gut sind. Den Code habe ich dann eingebunden, erhalte aber folgende Fehlermeldung:
Please choose and enter a password: hulk
Traceback (most recent call last):
File "C:\Python26\game\character_generation.py", line 70, in <module>
player = Player(login_name, password_hash)
File "C:\Python26\game\classes.py", line 55, in __init__
cursor.execute(sql_inserter, data_inserter)
ProgrammingError: You must not use 8-bit bytestrings unless you use a text_factory that can interpret 8-bit bytestrings (like text_factory = str). It is highly recommended that you instead just switch your application to Unicode strings.
Bis zu Zeile 55 passiert folgendes:
Code: Alles auswählen
#! C:\\Python26\game
# -*- coding: iso-8859-15 -*-
import sqlite3
import random
import hmac
from string import letters, digits
def create_salt(length=8, include=letters + digits, exclude="$"):
chars = list(set(include) ^ set(exclude))
return "".join(random.sample(chars, length))
def create_password_hash(password):
salt = create_salt()
h = hmac.new(password, salt).hexdigest()
return 'sha1${salt}${hash}'.format(salt=salt, hash=h)
def check_password_hash(password, password_hash):
method, salt, h = password_hash.split('$')
return h == hmac.new(password, salt).hexdigest()
class Player(object):
Players_total = 0
def __init__(self, login_name, password_hash):
self.Login_name = login_name
self.Login_password_hash = password_hash
self.Chr_firstname = None
self.Chr_lastname = None
self.Chr_sex = None
self.Chr_occupation = None
self.Chr_charisma = 0
self.Chr_endurance = 0
self.Chr_intelligence = 0
self.Chr_knowledge = 0
self.Chr_psyche = 0
self.Chr_reflexes = 0
self.Chr_experience = 0
self.Chr_credits = 100
self.Chr_fame = 0
Player.Players_total += 1
chr_experience = self.Chr_experience
chr_credits = self.Chr_credits
chr_fame = self.Chr_fame
data_inserter = {
"login_name" : login_name, "password_hash" : password_hash, "chr_experience" : chr_experience, "chr_credits" : chr_credits, "chr_fame" : chr_fame
}
sql_inserter = "INSERT INTO players VALUES (null, :login_name , :password_hash, null, null, null, null, null, null, null, null, null, null, :chr_experience, :chr_credits, :chr_fame)"
connection = sqlite3.connect("players.db")
cursor = connection.cursor()
cursor.execute(sql_inserter, data_inserter)
connection.commit()
Generell habe ich ein Verständnisproblem mit Unicode. Diese Zeile am Anfang, hat zumindest in IDLE für eine Ausgabe von Umlauten gesorgt:
Deshalb dachte ich, dass ich einzelne Strings nicht mehr in Unicode formatieren muss.
Allerdings weiß ich auch nicht wie ich u"irgendwas" zusammen mit raw_input benutze. Wie bringe ich raw_input dazu etwas als unicode anzunehmen?
Der Gesamtcode war leider zu lange um ihn hier im Forum abzubilden. Sonst hätte ich euch den jetzt zur Manöverkritik vorgelegt.
Verfasst: Samstag 27. Dezember 2008, 23:55
von BlackJack
Was sollen die ganzen Präfixe bei den Namen? `Chr_*`? Char? Christian? Warum fangen Attributnamen mit einem Grossbuchstaben an?
Was machst Du mit `Player.Players_total`? Falls Du in der Klasse eine `__del__()`-Methode zum runterzählen hast: Vergiss es, das funktioniert nicht zuverlässig.
Man kann bei Datenstrukturen auch mal Zeilenumbrüche verwenden. `sql_inserter` und `data_inserter` haben komische Namen und die Zeilen sind zu lang.
Die `str`-Exemplare, die `raw_input()` liefert müsstest Du erst dekodieren. Die passende Kodierung darfst Du raten.

Man sollte auf jedenfall dem Benutzer erlauben die anzugeben.
Ich bin mir auch nicht so ganz sicher, ob die Klasse so besonders praktisch ist. Wie erstellt man ein `Player`-Exemplar für einen Spieler, der schon in der Datenbank vorhanden ist?
Verfasst: Sonntag 28. Dezember 2008, 00:48
von DasIch
Eventuell hilft dass
Unicode Howto etwas Licht ins dunkel zu bringen.
Verfasst: Sonntag 28. Dezember 2008, 01:56
von arien101
BlackJack hat geschrieben:Was sollen die ganzen Präfixe bei den Namen? `Chr_*`? Char? Christian? Warum fangen Attributnamen mit einem Grossbuchstaben an?
chr soll die Abkürzung von "character" sein. Das Buch das ich durcharbeite lässt die Attributnamen mit Großbuchstaben anfangen. Dachte deshalb das muss so sein.
BlackJack hat geschrieben:Was machst Du mit `Player.Players_total`? Falls Du in der Klasse eine `__del__()`-Methode zum runterzählen hast: Vergiss es, das funktioniert nicht zuverlässig.
Damit wollte ich ursprünglich die Gesamtzahl aller Player zählen. Habe das dann aber über die Datenbank gemacht.
BlackJack hat geschrieben:Man kann bei Datenstrukturen auch mal Zeilenumbrüche verwenden. `sql_inserter` und `data_inserter` haben komische Namen und die Zeilen sind zu lang.
Danke für den Hinweis. War mir nicht sicher ob ich da irgendein Zeichen einfügen muss beim Umbruch.
BlackJack hat geschrieben:Die `str`-Exemplare, die `raw_input()` liefert müsstest Du erst dekodieren. Die passende Kodierung darfst Du raten.

Man sollte auf jedenfall dem Benutzer erlauben die anzugeben.
Oh, toll
BlackJack hat geschrieben:Ich bin mir auch nicht so ganz sicher, ob die Klasse so besonders praktisch ist. Wie erstellt man ein `Player`-Exemplar für einen Spieler, der schon in der Datenbank vorhanden ist?
Hmm, ja, das geht nicht. Der Grund warum ich hier überhaupt eine Klasse verwende war ja eigentlich, weil ich gerade an der entsprechenden Stelle im Buch war. Deshalb hatte ich mir wenig Gedanken über die Sinnhaftigkeit gemacht. Player war eben das einzige was für eine Klasse in Frage kam.
DasIch hat geschrieben:
Eventuell hilft dass Unicode Howto etwas Licht ins dunkel zu bringen.
Hab ich schonmal gelesen, aber das hat leider nicht zum Verständnis geführt. Werd' ich nochmal lesen. Vielleicht kapier ich's beim 2. Mal.
Verfasst: Sonntag 28. Dezember 2008, 02:19
von Leonidas
arien101 hat geschrieben:BlackJack hat geschrieben:Was sollen die ganzen Präfixe bei den Namen? `Chr_*`? Char? Christian? Warum fangen Attributnamen mit einem Grossbuchstaben an?
chr soll die Abkürzung von "character" sein. Das Buch das ich durcharbeite lässt die Attributnamen mit Großbuchstaben anfangen. Dachte deshalb das muss so sein.
Nein, das zeigt eher dass der Autor ungarische Notation nicht kapiert hat. Die in Python sowieso wenig Sinn macht.
Verfasst: Sonntag 28. Dezember 2008, 10:40
von arien101
DasIch hat geschrieben:Eventuell hilft dass
Unicode Howto etwas Licht ins dunkel zu bringen.
Nach dem 2. durchlesen habe ich jetzt versucht alle Strings in Unicode umzuwandeln. Besonders auch das eingegebene Passwort. Als Encoding habe ich iso-8859-15 genommen, da Europa ausreichen müsste. Daraufhin bekomme ich jetzt eine andere Fehlermeldung.
Code: Alles auswählen
while True:
login_password = getpass.getpass("""
Please choose and enter a password: """).decode('iso-8859-15')
if len(login_password) >= 4:
break
else:
print """
Your password must be at least 4 characters long!.
"""
continue
# Add random parameter to password and save it as a hash
password_hash = create_password_hash(login_password)
player = Player(login_name, password_hash)
Der resultierende Fehler:
Please choose and enter a password: beispiel
Traceback (most recent call last):
File "C:\Python26\game\character_generation.py", line 76, in <module>
password_hash = create_password_hash(login_password)
File "C:\Python26\game\classes.py", line 15, in create_password_hash
h = hmac.new(password, salt).hexdigest()
File "C:\Python26\lib\hmac.py", line 133, in new
return HMAC(key, msg, digestmod)
File "C:\Python26\lib\hmac.py", line 72, in __init__
self.outer.update(key.translate(trans_5C))
TypeError: character mapping must return integer, None or unicode
Was ist mit character mapping gemeint?
Verfasst: Sonntag 28. Dezember 2008, 15:33
von DasIch
hmac.new akzeptiert (afaik seit Python 2.6) keine Unicode Strings mehr sondern nur noch Byte Strings.