Как присвоить несколько значений одному ключу, используя словарь?

У меня есть 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" открывает файл для добавления; любые данные, записанные в файл, автоматически добавляются в конец.

Другие вопросы по тегам