I am using Redis Object Mapper(ROM) here.
This is my model
class User(rom.Model):
name = rom.String(required=True, unique=True)
nickname = rom.String(required=False)
photo = rom.String(required=False)
I am trying -
user1 = User(name="Ankush", nickname="iamkhush",
photo='http://graph.facebook.com/iamkhush/picture')
user1.save()
I get the result when I do
user = User.get(1) #user is a model instance
But when I do
user_obj = User.query.filter(name='Ankush').execute()
I get [ ] (An empty result)
Cant get why is this happening?
There are two parts to this. First, you don't need to use the index if you want to get an item by a column defined with unique=True, but the query is different. Using unique=True and index=False, you can get the item by using the User.get_by() form:
>>> User.get_by(name="Ankush")
<__main__.User object at 0x87070cc>
The primary limitation being that you must pass the full column exactly as it is defined in the column. This is generally useful for keeping unique email addresses (lowercase them first!), usernames (be careful with your capitalization), and other examples.
The second part is that when enabling the index, the index has "case-insensitive unique bag-of-words" semantics for string/text columns (I come from the search engine side of the world, which has substantially different (and arguably better) semantics than typical db queries). So if you want to find entries, you need to use:
>>> class User(rom.Model):
... name = rom.String(required=True, unique=True, index=True)
... nickname = rom.String(required=False)
... photo = rom.String(required=False)
...
>>> user1 = User(name="Ankush", nickname="iamankush", photo="http://graph.facebook.com/iamkhush/picture")
>>> user1.save()
>>> User.query.filter(name="ankush").all()
[<__main__.User object at 0x870738c>]
(also note that .execute() is an alias for .all() ).
If you want to change the case-sensitivity or other behavior with columns defined with index=True, you need to pass a custom keygen argument (you can see rom.util._string_keygen() at https://github.com/josiahcarlson/rom/blob/master/rom/util.py#L149 for rom.String and rom.Text key generation semantics).
You need to pass index=True along with name to allow it to be queried by filter.
class User(rom.Model):
name = rom.Text(required=True, unique=True,index = True,keygen=text_keyge)
nickname = rom.String(required=False)
photo = rom.String(required=False)
Related
I have two models in my application and I am trying to update a M2M field . Django documentation says that it can be done using set() method . But I guess by dealt set() accepts pk of the objects as arguments and what I want to do is set it using the actual values .
For e.g. I have a client in Clients models named "adam" and I want to set the value for its corresponding M2M field "items_onsale" to ["ac" , "book"]
Below is my code for the models :-
from django.db import models
class Client(models.Model):
SEX_CHOICES = [('M', 'Male'), ('F', 'Female')]
fname = models.CharField(max_length=100)
lname = models.CharField(max_length=100)
mailid = models.EmailField(max_length=100)
sex = models.CharField(max_length=1, choices=SEX_CHOICES, blank=True)
age = models.IntegerField()
items_onsale = models.ManyToManyField('Sinfo', blank=True)
def __str__(self): # for displaying the variable correctly.
return self.fname , self.lname , self.mailid , self.sex , self.age , self.items_onsale
class Sinfo(models.Model): # data of items put on sale by clients
iname = models.CharField(max_length=100)
idesc = models.TextField(max_length=300, null=True)
def __str__(self): # for displaying the variable correctly.
return self.iname
What I have tried till now is :-
c = Client.objects.get(pk=17)
list=["ac","book"]
c.items_onsale_set(list)
And I am getting below error :-
ValueError: Field 'id' expected a number but got 'book'
I know that there is a way to update it using values but not sure how . The django documentation does suggest using "through_defaults" option but haven't given any such example , so I am quite not sure how to use it :-
For many-to-many relationships set() accepts a list of either model instances or field values, normally primary keys, as the objs argument.
Use the through_defaults argument to specify values for the new intermediate model instance(s), if needed. You can use callables as values in the through_defaults dictionary and they will be evaluated once before creating any intermediate instance(s).
I am there is a better to achieve this , please let me know .
through_defaults option works if you have multiple foreign-keys in the same model when using the through option.
in your case, the items_onsale have a direct relationship with the client, use the add function instead of set.
c = Client.objects.get(pk=17)
list=["ac","book"]
c.items_onsale.set(list)
to
c = Client.objects.get(pk=17)
list= Sinfo.objects.filter(iname__in=["ac","book"])
c.items_onsale.add(*list)
add() accepts an arbitrary number of arguments, not a list of them.
To expand that list into arguments, use *
Previously, I have the following entity
class User(ndb.Model):
name = ndb.StringProperty(required = True)
timestamp = ndb.DateTimeProperty(required = True)
I use name as id, by written my code in the following way
user = User.get_or_insert(name, name=name,
timestamp=datetime.datetime.fromtimestamp(user_timestamp))
By having name as id, I can perform fast read or update using name.
Now, I decide to change my entity to the following, and have both name and type as composite id.
class User(ndb.Model):
name = ndb.StringProperty(required = True)
type = ndb.StringProperty(required = True)
timestamp = ndb.DateTimeProperty(required = True)
After referring https://stackoverflow.com/a/5454623/72437
My first thought is, by concatenation both name and type, it achieve my composite id purpose.
user = User.get_or_insert(name+type, name=name, type=type
timestamp=datetime.datetime.fromtimestamp(user_timestamp))
Soon, I realize this idea is flaw. As the following situation will create conflicting.
name type id
-------------------------
cheok paid cheokpaid
cheokp aid cheokpaid
2 different users end up with same id.
I was wondering, what is the proper way for me
To have composite id based on name and type
Have fast read or update, using name and type
Why not just use a separator while concatenating name and type. Then your code will work as is and solve your problem.
For example if you use '_' as your separator then
cheok paid become cheok_paid and
cheokp aid become cheokp_aid
I'm trying to make this table with a clickable field which changes the boolean for the entry to its opposite value. It works, but I want an alternative text as "False" or "True" does not look nice, and the users are mainly Norwegian.
def bool_to_norwegian(boolean):
if boolean:
return "Ja"
else:
return "Nei"
class OrderTable(tables.Table):
id = tables.LinkColumn('admin_detail', args=[A('id')])
name = tables.Column()
address = tables.Column()
order = tables.Column()
order_placed_at = tables.DateTimeColumn()
order_delivery_at = tables.DateColumn()
price = tables.Column()
comment = tables.Column()
sent = tables.LinkColumn('status_sent', args=[A('id')])
paid = tables.LinkColumn('status_paid', args=[A('id')], text=[A('paid')])
class Meta:
attrs = {'class': 'order-table'}
If you look under the "paid" entry I am testing this right now, why can't I access the data with the same accessor as I do in the args? If I change the args to args=[A('paid')] and look at the link, it does indeed have the correct data on it. The model names are the same as the ones in this table, and "paid" and "sent" are BooleanFields.
This is kind of what I ultimately want:
text=bool_to_norwegian([A('paid')])
Here is what I send to the table:
orders = Order.objects.order_by("-order_delivery_at")
orders = orders.values()
table = OrderTable(orders)
RequestConfig(request).configure(table)
The text argument expects a callable that accepts a record, and returns a text value. You are passing it a list (which it will just ignore), and your function is expecting a boolean instead of a record. There is also no need for using accessors here.
Something like this should work:
def bool_to_norwegian(record):
if record.paid:
return "Ja"
else:
return "Nei"
Then in your column:
paid = tables.LinkColumn('status_paid', text=bool_to_norwegian)
(Note, it is not clear from your question where the data is coming from - is paid a boolean? You may need to adjust this to fit).
As an aside, the way you are passing args to your columns is weird (it seems the documentation also recommends this, but I don't understand why - it's very confusing). A more standard approach would be:
id = tables.LinkColumn('admin_detail', A('id'))
or using named arguments:
id = tables.LinkColumn('admin_detail', accessor=A('id'))
I have the following models:
class AcademicRecord(models.Model):
record_id = models.PositiveIntegerField(unique=True, primary_key=True)
subjects = models.ManyToManyField(Subject,through='AcademicRecordSubject')
...
class AcademicRecordSubject(models.Model):
academic_record = models.ForeignKey('AcademicRecord')
subject = models.ForeignKey('Subject')
language_group = IntegerCharField(max_length=2)
...
class SubjectTime(models.Model):
time_id = models.CharField(max_length=128, unique=True, primary_key=True)
subject = models.ForeignKey(Subject)
language_group = IntegerCharField(max_length=2)
...
class Subject(models.Model):
subject_id = models.PositiveIntegerField(unique=True,primary_key=True)
...
The academic records have list of subjects each with a language code and the subject times have a subject and language code.
With a given AcademicRecord, how can I get the subject times that matches with the AcademicRecordSubjects that the AcademicRecord has?
This is my approach, but it makes more queries than needed:
# record is the given AcademicRecord
times = []
for record_subject in record.academicrecordsubject_set.all():
matched_times = SubjectTime.objects.filter(subject=record_subject.subject)
current_times = matched_times.filter(language_group=record_subject.language_group)
times.append(current_times)
I want to make the query using django ORM not with raw SQL
SubjectTime language group has to match with Subject's language group aswell
I got it, in part thanks to #Robert Jørgensgaard Eng
My problem was how to do the inner join using more than 1 field, in which the F object came on handly.
The correct query is:
SubjectTime.objects.filter(subject__academicrecordsubject__academic_record=record,
subject__academicrecordsubject__language_group=F('language_group'))
Given an AcademicRecord instance academic_record, it is either
SubjectTime.objects.filter(subject__academicrecordsubject_set__academic_record=academic_record)
or
SubjectTime.objects.filter(subject__academicrecordsubject__academic_record=academic_record)
The results reflect all the rows of the join that these ORM queries become in SQL. To avoid duplicates, just use distinct().
Now this would be much easier, if I had a django shell to test in :)
As the title says, I need a way to perform this query. I have tried the following:
user_list_ids = []
user_lists = []
user_entries = OwnerEntry.objects.filter(name=request.user)
for user in user_entries:
user_list_ids.append(user.list_id)
user_lists = ListEntry.objects.filter(id__in=user_list_ids)
for user in user_entries:
user_list_ids.append(user.list_id)
user_lists = ListEntry.objects.filter(id__in=user_list_ids)
However, I get an error on the last line: int() argument must be a string or a number, not 'ListEntry'
Here are the relevant models:
class OwnerEntry(models.Model):
name = models.CharField(max_length=32)
list_id = models.ForeignKey(ListEntry)
class Meta:
ordering = ('name',)
class ListEntry(models.Model):
name = models.CharField(max_length=64)
# active_date = models.DateTimeField('date of last list activity')
expire_date = models.DateField('date of expiration')
create_date = models.DateField('date created')
to answer your question directly, please note that you have a list_id rather than list as a ForeignKey name (OwnerEntry model). In order to actually extract the fk value, you should use list_id_id instead (or rename list_id to list ;))
Please also note that django supports object references, like so:
someowner = OwnerEntry.objects.get( ... )
ownerslist = someowner.listentry_set.all()
cheers!
You can define OwnerEntry's foreign key to ListEntry as :
list_id = models.ForeignKey(ListEntry, related_query_name='owner_entry')
and then do this one-liner in your code:
user_lists = ListEntry.objects.filter(owner_entry__name=request.user)
What this does is exactly filter every ListEntry which has at least one owner_entry whose name is equal to request.user's.
The redefinition of the foreign key is just for the sake of giving a nice name to the query attribute.
For more details on queries that work with backward relationships: https://docs.djangoproject.com/en/dev/topics/db/queries/#lookups-that-span-relationships