How to use unique_together method in django views - python

class Model1(models.Model):
username = models.CharField(max_length=100,null=False,blank=False,unique=True)
password = models.CharField(max_length=100,null=False,blank=False)
class Model2(models.Model):
name = models.ForeignKey(Model1, null=True)
unique_str = models.CharField(max_length=50,null=False,blank=False,unique=True)
city = models.CharField(max_length=100,null=False,blank=False)
class Meta:
unique_together = (('name', 'unique_str'),)
I've already filled 3 sample username-password in Model1 through django-admin page
In my views I'm getting this list as
userlist = Model1.objects.all()
#print userlist[0].username, userlist[0].password
for user in userlist:
#here I want to get or create model2 object by uniqueness defined in meta class.
#I mean unique_str can belong to multiple user so I'm making name and str together as a unique key but I dont know how to use it here with get_or_create method.
#right now (without using unique_together) I'm doing this (but I dont know if this by default include unique_together functionality )
a,b = Model2.objects.get_or_create(unique_str='f3h6y67')
a.name = user
a.city = "acity"
a.save()

What I think you're saying is that your logical key is a combination of name and unique_together, and that you what to use that as the basis for calls to get_or_create().
First, understand the unique_together creates a database constraint. There's no way to use it, and Django doesn't do anything special with this information.
Also, at this time Django cannot use composite natural primary keys, so your models by default will have an auto-incrementing integer primary key. But you can still use name and unique_str as a key.
Looking at your code, it seems you want to do this:
a, _ = Model2.objects.get_or_create(unique_str='f3h6y67',
name=user.username)
a.city = 'acity'
a.save()
On Django 1.7 you can use update_or_create():
a, _ = Model2.objects.update_or_create(unique_str='f3h6y67',
name=user.username,
defaults={'city': 'acity'})
In either case, the key point is that the keyword arguments to _or_create are used for looking up the object, and defaults is used to provide additional data in the case of a create or update. See the documentation.
In sum, to "use" the unique_together constraint you simply use the two fields together whenever you want to uniquely specify an instance.

Related

Django: join two table on foreign key to third table?

I have three models
class A(Model):
...
class B(Model):
id = IntegerField()
a = ForeignKey(A)
class C(Model):
id = IntegerField()
a = ForeignKey(A)
I want get the pairs of (B.id, C.id), for which B.a==C.a. How do I make that join using the django orm?
Django allows you to reverse the lookup in much the same way that you can use do a forward lookup using __:
It works backwards, too. To refer to a “reverse” relationship, just use the lowercase name of the model.
This example retrieves all Blog objects which have at least one Entry whose headline contains 'Lennon':
Blog.objects.filter(entry__headline__contains='Lennon')
I think you can do something like this, with #Daniel Roseman's caveat about the type of result set that you will get back.
ids = B.objects.prefetch_related('a', 'a__c').values_list('id', 'a__c__id')
The prefetch related will help with performance in older versions of django if memory serves.

How to create the related field?

I have a Model you see bellow:
class PriceModel(models.Model):
name = models.CharField(max_length=12)
the PriceModel's id is increased from 1. I want to add other field, which is related to the id. Such as I want it to #PM1, #PM2... means the #PM + id.
How to create the related field?
I want to add other field, which is related to the id. Such as I want it to #PM1, #PM2... means the #PM + id.
First of all, it is not guaranteed that for all database systems, the id is always incremented with one. For example if the database works with transactions, or other mechanisms, it is possible that a transaction is rolled back, and hence the corresponding id is never taken.
If you however want some "field" that always depends on the value of a field (here id), then I think a #property is probably a better idea:
class PriceModel(models.Model):
name = models.CharField(max_length=12)
#property
def other_field(self):
return '#PM{}'.format(self.id)
So now if we have some PriceModel, then some_pricemodel.other_field will return a '#PM123' given (id is 123). The nice thing is that if the id is changed, then the property will change as well, since it is each time calculated based on the fields.
A potential problem is that we can not make queries with this property, since the database does not know such property exists. We can however define an annotation:
from django.db.models import Value
from django.db.models.functions import Concat
PriceModel.objects.annotate(
other_field=Concat(Value('#PM'), 'id')
).filter(
other_field__icontains='pm1'
)
I think you can in your model serializer add a special field, rather than add a field in the Model.
class PriceModelSerializer(ModelSerializer):
....
show_id = serializers.SerializerMethodField(read_only=True)
class Meta:
model = PriceModel
def get_show_id(self):
return '#PM' + self.id

Django models: set default IntegerField to number of instances

I would like to set the "order" IntegerField of my Achievement model to the current count of objects in Achievement. The order field is used to order achievements, and users can change it. For now I have 1 as default.
class Achievement(models.Model):
title = models.CharField(max_length=50, blank=True)
description = models.TextField()
order = models.IntegerField(default=1) #Get the number of achievement objects
class Meta:
db_table = 'achievement'
ordering = ['order', 'id']
For example, if I already have one achievement in my database with whatever order the next one should get order=2 by default.
As far as I understood, you want to have a default value of 1 to the order integer field and increment it with each entry of Achievment (same functionality as the id), but also allow users change it.
For this purpose you can use Django's AutoField:
An IntegerField that automatically increments according to available IDs. You usually won’t need to use this directly; a primary key field will automatically be added to your model if you don’t specify otherwise.
Like this:
class Achievement(models.Model):
...
order = models.AutoField(default=1, primary_key=False)
# Also specify that this autofield is *not* a ^ primary key
class Meta:
ordering = ['order', 'id']

Django: list all reverse relations of a model

I would like my django application to serve a list of any model's fields (this will help the GUI build itself).
Imagine the classes (ignore the fact that all field of Steps could be in Item, I have my reasons :-) )
class Item(models.Model):
name = models.CharField(max_length=100)
description = models.TextField()
class Steps(models.Model):
item = models.OneToOneField('Item', related_name='steps')
design = models.BooleanField(default=False)
prototype = models.BooleanField(default=False)
production = models.BooleanField(default=False)
Now, when I want to list a model's fields:
def get_fields(model):
return model._meta.fields + model._meta.many_to_many
But I would also like to get the list of "related" one-to-one foreign keys to my models. In my case Item.steps would not be in that list.
I have found that model._meta.get_all_field_names does include all the related fields.
But when I call Item._meta.get_field_by_name('steps') it returns a tuple holding a RelatedObject, which does not tell me instantly whether this is a single relation or a one-to-many (I want to list only reversed one-to-one relations).
Also, I can use this bit of code:
from django.db.models.fields.related import SingleRelatedObjectDescriptor
reversed_f_keys = [attr for attr in Item.__dict__.values() \
if isinstance(attr, SingleRelatedObjectDescriptor)]
But I'm not very satisfied with this.
Any help, idea, tips are welcome!
Cheers
This was changed (in 1.8 I think) and Olivier's answer doesn't work anymore. According to the docs, the new way is
[f for f in Item._meta.get_fields()
if f.auto_created and not f.concrete]
This includes one-to-one, many-to-one, and many-to-many.
I've found out that there are methods of Model._meta that can give me what I want.
my_model = get_model('app_name','model_name')
# Reverse foreign key relations
reverse_fks = my_model._meta.get_all_related_objects()
# Reverse M2M relations
reverse_m2ms = my_model._meta.get_all_related_many_to_many_objects()
By parsing the content of the relations, I can guess whether the "direct" field was a OneToOneField or whatever.
I was looking into this answer as a starting point to identify reversed relationships for a model instance.
So, I noticed that when you get all the fields using instance._meta.get_fields(), those that are direct relationships, which are 3 types (ForeignKey, ManyToMany, OneTone), their parent class (field.__class__.__bases__) is django.db.models.fields.related.ForeignKey.
However, those that are reverse relationships inherit from django.db.models.fields.reverse_related.ForeignObjectRel. And if you take a look at this class, it has:
auto_created = True
concrete = False
So you could identify those by the attributes mentioned in the top-rated answer or by asking isinstance(field, ForeignObjectRel.
Another thing I could notice is that those reverse relationships have a field attribute which points to the direct relationship generating that reverse relationship.
Additionally, in order to exclude the fields instantiating the through table, those have through and through_fields attributes
And what about this :
oneToOneFieldNames = [
field_name
for field_name in Item._meta.get_all_field_names()
if isinstance(
getattr(
Item._meta.get_field_by_name(field_name)[0],
'field',
None
),
models.OneToOneField
)
]
RelatedObject may have a Field attribute for relations. You just have to check if this is a OneToOne field and you can retrieve only what you want
if you are using Django Rest Framework, you could use something like that for your obj:
from rest_framework.utils import model_meta
info = model_meta.get_field_info(obj)
for field in obj.__class__.__dict__.keys():
if field in info.relations and info.relations[field].to_many and info.relations[field].reverse:
#print all reverse relations
print(field)

Getting the first item item in a many-to-many relation in Django

I have two models in Django linked together by ManyToMany relation like this:
class Person(models.Model):
name = models.CharField(max_length=128)
class Group(models.Model):
name = models.CharField(max_length=128)
members = models.ManyToManyField(Person)
I need to get the main person in the group which is the first person in the group. How can I get the first person?
Here's how I'm adding members:
grp = Group.objects.create(name="Group 1")
grp.save()
prs = Person.objects.create(name="Tom")
grp.members.add(prs) #This is the main person of the group.
prs = Person.objects.create(name="Dick")
grp.members.add(prs)
prs = Person.objects.create(name="Harry")
grp.members.add(prs)
I don't think I need any additional columns as the id of the table group_members is a running sequence right.
If I try to fetch the main member of the group through Group.objects.get(id=1).members[0] then Django says that the manager is not indexable.
If I try this Group.objects.get(id=1).members.all().order_by('id')[0], I get the member with the lowest id in the Person table.
How can I solve this?
Thanks
Django doesn't pay attention to the order of the calls to add. The implicit table Django has for the relationship is something like the following if it was a Django model:
class GroupMembers(models.Model):
group = models.ForeignKey(Group)
person = models.ForeignKey(Person)
Obviously, there's nothing there about an "order" or which you should come first. By default, it will probably do as you describe and return the lowest pk, but that's just because it has nothing else to go off of.
If you want to enforce an order, you'll have to use a through table. Basically, instead of letting Django create that implicit model, you create it yourself. Then, you'll add a field like order to dictate the order:
class GroupMembers(models.Model):
class Meta:
ordering = ['order']
group = models.ForeignKey(Group)
person = models.ForeignKey(Person)
order = models.PositiveIntegerField(default=0)
Then, you tell Django to use this model for the relationship, instead:
class GroupMembers(models.Model):
group = models.ForeignKey(Group)
person = models.ForeignKey(Person, through='GroupMembers')
Now, when you add your members, you can't use add anymore, because additional information is need to complete the relationship. Instead, you must use the through model:
prs = Person.objects.create(name="Tom")
GroupMembers.objects.create(person=prs, group=grp, order=1)
prs = Person.objects.create(name="Dick")
GroupMembers.objects.create(person=prs, group=grp, order=2)
prs = Person.objects.create(name="Harry")
GroupMembers.objects.create(person=prs, group=grp, order=3)
Then, just use:
Group.objects.get(id=1).members.all()[0]
Alternatively, you could simply add a BooleanField to specify which is the main user:
class GroupMembers(models.Model):
group = models.ForeignKey(Group)
person = models.ForeignKey(Person)
is_main_user = models.BooleanField(default=False)
Then, add "Tom" like:
prs = Person.objects.create(name="Tom")
GroupMembers.objects.create(person=prs, group=grp, is_main_user=True)
And finally, retrieve "Tom" via:
Group.objects.get(id=1).members.filter(is_main_user=True)[0]
Relational database don't have any notion of ordering, by default. There's no such thing as "first". You need to add an explicit "order" field which keeps the order, and order by it.
You were pretty close:
Group.objects.get(id=1).members.all()[0]
(But as Alex Gaynor says in his answer, for predictable behaviour you need a proper field to sort on)
to reliably keep a record of who is the "main" person in a group, you may want to have a look at using a through table to keep this metadata

Categories