Как присвоить несколько значений одному ключу, используя словарь?
У меня есть HTML-форма, которая имеет Firstname
, LastName
, Age
а также Gender
и ADD
кнопка. Я ввожу данные в форму, и они попадают в базу данных Berkeelys. Мой код печатает только последние значения. Я хочу, чтобы он показывал все значения, связанные с конкретным ключом
#!/usr/bin/python
import bsddb
import cgi
form = cgi.FieldStorage()
print "Content-type:text/html\n"
Fname = form.getvalue('firstname', '')
Lname = form.getvalue('lastname', '')
Age = form.getvalue('age', 0)
Gender = form.getvalue('gender', '')
#print Fname, Lname, Age
db = bsddb.hashopen("/home/neeraj/public_html/database/mydb.db","w")
db['FirstName'] = Fname
db['LastName'] = Lname
db['Age'] = Age
db['Gender'] = Gender
db.close()
db = bsddb.hashopen("/home/neeraj/public_html/database/mydb.db","r")
#db = bsddb.hashopen("/home/neeraj/public_html/database/mydb.db")
print db['FirstName'], db['LastName'], db['Age'], db['Gender']
db.close()
print db
5 ответов
Вы должны использовать базу данных SQL вместо dict
на основе интерфейса, поскольку базы данных SQL уже обрабатывают несколько кортежей в таблице.
Во всяком случае, если вы хотите иметь dict
Интерфейс вы можете использовать shelve
модуль (bsddb
устарела, поэтому вам следует избегать этого) и сохранять каждое значение в list
:
import shelve
COLUMNS = ('FirstName', 'LastName', 'Age', 'Sex')
the_db = shelve.open('test.db', writeback=True)
for col_name in COLUMNS:
if col_name not in the_db:
the_db[col_name] = []
records = [
('John', 'Deer', 20, 'M'),
('Ada', 'Lovelace', 23, 'F'),
]
for record in records:
for col_name, value in zip(COLUMNS, record):
the_db[col_name].append(value)
the_db.close()
the_db = shelve.open('test.db')
for record in zip(*(the_db[col_name] for col_name in COLUMNS)):
print(record)
the_db.close()
Вышеприведенный код выводит:
('John', 'Deer', 20, 'M')
('Ada', 'Lovelace', 23, 'F')
Если вы хотите использовать базу данных SQL, вы можете использовать sqlite3
модуль. Например:
import sqlite3
conn = sqlite3.connect('test.sqlite')
cursor = conn.cursor()
cursor.execute('''
CREATE TABLE people (
FirstName text,
LastName text,
Age int,
Sex text
)''')
cursor.execute('''
INSERT INTO people values ('John', 'Deer', 20, 'M')''')
cursor.execute('''
INSERT INTO people values ('Ada', 'Lovelace', 23, 'F')''')
conn.commit()
for record in cursor.execute('''SELECT * FROM people'''):
print(record)
Вышеприведенный код выводит:
(u'John', u'Deer', 20, u'M')
(u'Ada', u'Lovelace', 23, u'F')
(Обратите внимание u'...'
просто означает, что строки являются Unicode, это не меняет их значение)
Однако у этого кода есть некоторые проблемы (например, попробуйте запустить его дважды...), но если вы хотите пойти по этому пути, то сначала вы должны изучить SQL, так что продолжайте и изучайте его (есть много онлайн-учебников. Например, w3schools
из них).
Правильный способ связать несколько значений с одним ключом - это упаковать значение списка или словаря, используя, например, "json.dumps".
Вот пример:
#!/usr/bin/python
from json import dumps
from json import loads
import bsddb
# write
users = bsddb.hashopen("users.db","w")
primarykey = 'amz'
users[primarykey] = dumps(dict(username="amz", age=30, bio="craftsman"))
users.close()
# read
users = bsddb.hashopen("users.db","r")
for key in users.keys():
print loads(users[key])
users.close()
Это базовый шаблон, который будет использоваться с bsddb и применим к другим db-ключам / значениям, таким как leveldb.
Дополнительно:
Учитывая тот факт, что ключи хеш-таблицы bsddb упорядочены лексикографически (то есть, как строки python 2), вы можете создавать хеш-карты с ключами, имеющими предсказуемый порядок, избавляя вас от необходимости просматривать всю таблицу.
Чтобы эффективно использовать эту функцию, вы должны создать полезные ключи. Опять же, вам нужна функция упаковки, которая переводит порядок сортировки в Python в лексикографический порядок (т.е. 11 > 2
но "11" < "2"
) Вот пример такой функции упаковки:
def pack(*values):
def __pack(value):
if type(value) is int:
return '1' + struct.pack('>q', value)
elif type(value) is str:
return '2' + struct.pack('>q', len(value)) + value
else:
data = dumps(value, encoding='utf-8')
return '3' + struct.pack('>q', len(data)) + data
return ''.join(map(__pack, values))
Это своего рода наивность, вы могли бы пройти лишнюю милю и поддержать float
и лучше упаковать int
чтобы сэкономить место.
Например, с учетом упрощенной схемы User(username, age)
Вы можете создать еще одну хэш-карту, которую мы называем age_index
с помощью которого вы можете легко найти каждого пользователя в возрасте 30 лет. Хэш-карта может выглядеть следующим образом:
key | value
-----------------
29 tom | X
30 amz | X
30 joe | X
30 moh | X
Это удобочитаемое представление hasmap: ключ фактически упакован с pack
функция. Как вы можете видеть, ключевым является состав age
и primarykey
предмета, сохраненного ранее. В этом случае значение не используется, потому что оно нам не нужно. Помните о том, что каждый ключ является и должен быть уникальным.
После того, как эта схема создана, вы делаете "выборочные запросы", называемые диапазонными запросами в bsddb, используя Cursor.set_range(key)
, Это установит курсор на ближайшую клавишу и вернет связанную пару ключ / значение (семантика может немного отличаться в зависимости от базы данных).
Например, чтобы получить первого человека, который имеет age=30
, используйте следующий код:
def get_first_person_with_age_thirty()
key, _ = age_index.set_range(pack(30)) # we don't need value
age, pk = unpack(key)
# set_range, will set the key to "30" and a pk
# or with a key prefix superior to 30.
# So we need to check that age is really 30.
if age == 30:
return loads(users[pk])
Это вернет документ, связанный с пользователем amz
,
Чтобы пойти дальше, нужно использовать другой интерфейс bsddb, точка входа которого bsddb.db.DB
а также bsddb.db.DBEnv
( документация ]. С помощью этого интерфейса вы можете иметь несколько хэш-карт, привязанных к одному и тому же контексту транзакции, даже если нет необходимости использовать транзакции.
Вы можете сохранить несколько значений для одного ключа в Беркли БД, установив флаг дублирования
filename = '/path/to/berkeley/db/file'
fruitDB = db.DB()
fruitDB.set_flags(db.DB_DUP)
fruitDB.open(filename, None, db.DB_BTREE, db.DB_CREATE)
fruitDB.put(str(1), "Waqar")
fruitDB.put(str(1), "Umer")
fruitDB.put(str(2), "x")
fruitDB.put(str(2), "y")
fruitDB.put(str(4), "z")
fruitDB.put(str(5), "e")
Но вы не можете получить их все, используя метод "Получить" в BDB, вам нужно использовать курсор, чтобы получить элементы, см. Документацию по этому вопросу, или вы можете получить все элементы, принадлежащие одному ключу, используя
cursor = fruitDB.cursor()
cursor.set(str(1))
record = cursor.current()
listTup = []
while record:
print record
listTup.append(record)
record = cursor.next_dup()
выход будет
('1', 'Waqar')
('1', 'Umer')
Это вернет список кортежей, в которых все значения принадлежат ключу "1". Надеюсь, это поможет.
Правильный ответ: не используйте BDB для такого случая. Для простых приложений вы можете использовать встроенный модуль sqlite, который был впервые представлен в python 2.5.
Я полагаю, открытие базы данных каждый раз w
вариант, вы перезаписываете каждый раз и сохраняете только последнюю запись. Вы должны вместо этого использовать a
,
db = bsddb.hashopen("/home/neeraj/public_html/database/mydb.db","a")
Выдержка из официального руководства по Python ->
open() returns a file object, and is most commonly used with two arguments:
open(filename, mode).
f = open ('workfile', 'w') Первый аргумент - это строка, содержащая имя файла. Второй аргумент - это другая строка, содержащая несколько символов, описывающих способ использования файла. режим может быть "r", когда файл будет только читаться, "w" только для записи (существующий файл с тем же именем будет удален), а "a" открывает файл для добавления; любые данные, записанные в файл, автоматически добавляются в конец.