I would like to be able to add and remove items from LANGUAGES, instead of them being hardcoded like this. Now I get what I need, which is DropDown with 'English', 'German' and 'Italian' choice. Now i need them to be in a SQLite database and retrived from there, with option to add new languages and remove/edit existing. Thanks in advance...
LANGUAGES = [
("1", "English"),
("2", "German"),
("3", "Italian"),
]
language = models.CharField(max_length=50, choices=LANGUAGES, default=1, null=False)
You do that with a ForeignKey [Django-doc], For example:
class Language(models.Model):
name = models.CharField(max_length=50, unique=True)
def __str__(self):
return self.name
class MyModel(models.Model):
language = models.ForeignKey(Language, on_delete=models.PROTECT)
You can then populate the database with records for Language, and select the language for the MyModel objects.
If you use a ModelForm, then standard Django will make a dropdown with the options in the "target" model (and use str(…) to represent these objects).
It is probably better to set unique=True [Django-doc] for the name field, to prevent creating another Language object with the same name.
By setting on_delete=models.PROTECT we prevent removing a language, given MyModel refers with at least one object to that language. So you can only remove languages if no MyModel is referring to it anymore.
The database will normally guarantee referential integrity. That means that the language column stores the value of the primary key of the object it refers to. The database normally guarantees that if one such column contains a value x, then there is a primary key with that value in the table for Language.
If you're looking to do this in a form, create a new model for languages and populate the table.
class Language(Model):
name = models.CharField(max_length=50)
class MyLanguageForm(ModelForm):
language = forms.ModelChoiceField(queryset=Languages.objects.all())
# The rest
Related
In our model we have a name and slug fields. This is how it looks like:
class MyModel(CommonInfo, SafeDeleteModel):
name = models.CharField(_('Name'), max_length=255, null=True, blank=True)
slug = AutoSlugField(_('Url'), populate_from='name', unique=True,)
For the slug field we generate an unique slug every time our model is saved.
We are also using Django Safe Delete library to soft delete our model:
Django Safe Delete
Django Autoslug
That means that for example if we create a new instance of our model with Name "My Model" it will auto generate a slug that will look like this: "/my-model".
Now let's say we "soft delete" this instance with the slug "/my-model". In our database there will be a property deleted which contains the date when the model was deleted. We don't show this one in our application, it is completely ignored (because it is soft deleted, that's fine).
The problem is that next time we create another one with the same name "My Model" it will auto generate the slug "/my-model" again, not considering that there is already one (which is soft deleted) with the same name and slug. We would need something like "/my-model-1" or whatever that is unique.
We are missing the connection between the autoslug and the safe-delete libraries, somehow the autoslug needs to know that there might be soft deleted ones and consider them when generating the unique slug.
Any help would be really appreciated and please consider that we are totally new in Django / Python.
if this doesn't work, our workaround will be generating the slug using 2 fields (name & id). It will generate a slug that will look like this: "/my-model/12345" and will be always unique since id is unique.
I think we found it.
We needed to create a new Manager that can see all the instances, even the soft deleted ones:
class MyModelAllManager(SafeDeleteManager):
_safedelete_visibility = DELETED_VISIBLE
Then in our model we pass it to the AutoSlugField function:
class MyModel(CommonInfo, SafeDeleteModel):
# We don't know why but it only works if we create a variable and assign the Manager to it. It doesn't work if we pass the Manager directly to the AutoSlugField function.
all_objects = MyModelAllManager()
name = models.CharField(_('Name'), max_length=255, null=True, blank=True)
slug = AutoSlugField(_('Url'), populate_from='name', unique=True, manager=all_objects)
That does the magic.
I am trying to write a generic method that can take any Django Model and returns it in a dictionary form.
So for example, if my models are defined thus (very generic):
class A(models.Model):
somefieldA = models.TextField()
m2mfield = models.ManyToManyField(B, through='AandB')
def __unicode__(self):
return self.somefieldA
class B(models.Model):
somefieldB = models.TextField()
def __unicode__(self):
return self.somefieldB
class AandB(models.Model):
a = models.ForeignKey(A)
b = models.ForeignKey(B)
field1 = models.DecimalField()
field2 = models.TextField()
field3 = models.DateField()
Now, assume we have an instance of the object A a_obj.
I can get all the related B objects using:
# This loop is there because I am working with other fields as well.
def instance_to_dict(instance):
for field in instance._meta.get_fields():
if field.many_to_many:
m2m_mgr = getattr(instance, field.name)
for idx, assoc_obj in enumerate(m2m_mgr.all()):
assoc_obj_str = str(assoc_obj)
# How to obtain the related through field values?
# m2m_mgr.through.objects.get() would need prior knowlegde
# of field name so get(a=instance, b=assoc_obj) is not possible
# m2m_mgr.through.objects.all() fetches all the objects
# in the Many to Many manager.
And then call instance_to_dict(a_obj). This method could be called by passing other models' instances.
Ideally, I would like to create a dict of the obj and related "through" fields for any object. Is this possible to do?
In addition to the explicitly defined ManyToMany manager, there is also an implicit reverse relationship for the ForeignKey from AandB to A. So you can do something like this:
for field in instance._meta.get_fields(include_hidden=True):
if field.one_to_many: # reverse ForeignKey
m2m_through_mgr = getattr(instance, field.get_accessor_name()) # e.g. aandb_set
m2m_through_mgr.all() # all related instances from the through table
Another approach is to go through the through table fields looking at field.related_model to see which one points back to your original table.
This all gets quite messy, but there should be enough meta information to do what you want. One obstacle is that the API isn't fully documented. Specifically, relation fields are represented by instances of the ManyToOneRel class, which as of Django 2.1 remains undocumented for reasons hinted at in the source code. Hence my use of the undocumented get_accessor_name() method.
I've defined language_tuples = models.ManyToManyField(LanguageTuple) in my UserProfile. This field should be filled when regular user want to became a translator. So he should be able to choose as many as needed tuples of languages - language by language.
EDIT: Thanks to Shang Wang, now I can choose multiple LanguageTuples but I'm not able to create new LanguageTuple objects inside the form.
class Language(models.Model):
shortcut = models.CharField(max_length=40)
name = models.CharField(max_length=40)
def __str__(self):
return self.name
class LanguageTuple(models.Model):
language_from = models.ForeignKey(Language, related_name='language_from', null=True)
language_to = models.ForeignKey(Language, related_name='language_to', null=True)
def __str__(self):
return '{} to {}'.format(self.language_from, self.language_to)
So let's assume that there are multiple Language objects in database already but no instances of LanguageTuple. I want user to be able to built his own tuples (as many as he wants). So if there were languages CZ,EN,GE,SK - he can built for example these tuples: CZ-EN, EN-CZ, GE-CZ, SK-GE etc. - after choosing tuples, those tuples are created inside the database as regular LanguageTuple instances if does not exists.
The problem is that there is no form field inside the form when it is rendered. Don't know what to do with that... as you can see, I've added field - language_tuples into the form.
class TranslatorRegistrationForm(forms.Form):
IBAN = forms.CharField(max_length=40,required=True)
first_name = forms.CharField(max_length=40,required=True)
last_name = forms.CharField(max_length=40,required=True)
class Meta:
model = UserProfile
fields = (
'first_name','last_name','IBAN','language_tuples'
)
One problem I've already mentioned in comment that you need forms.ModelForm for TranslatorRegistrationForm, otherwise django won't recognize all fields you want to display.
If you want user to choose from language_tuples as well as creating new pairs, it's going to be 2 forms. One for your existing form, the other is a form for model LanguageTuple. You need to display both forms in the template, so people could choose either from the list language_tuples or fill out the form for LanguageTuple.
Now be aware that you need some logic in place to detect whether user has chosen an existing language_tuple or trying to use a newly created LanguageTuple. It's some extra steps before you save everything to database but it should be straight forward.
When I checked group_cover table which is created by Django, there were group_id_id field and group_cover field.
I'd like to change group_id_id to group_id.
models.py
class Group(models.Model):
group_id = models.AutoField(primary_key=True)
group_name = models.CharField(max_length=50, unique=False, blank=False)
class Group_Cover(models.Model):
group_id = models.OneToOneField(Group, primary_key=True) # this create group_id_id
group_cover = models.ImageField(upload_to="/image/group/")
class Group_Member(models.Model):
user_id = models.ForeignKey(User2) # this create user_id_id
group_id = models.ForeignKey(Group) # this create group_id_id
Yeah, if I write,
group = models.OneToOneField(Group, primary_key=True)
It might work, but I may not need "_id" suffix on some field.
I read this document, but owing to my poor English, I couldn't understand the way.
Would you please teach me how to change?
Django adds an _id postix to primary keys that are generated automatically. You generally don't need to worry about them unless using a legacy data base.
Solution 2 would be the one i would recommend for a new project. Solution 1 for legacy databases.
Solution 1
To modify your existing code, use the following db_column attribute as it allows you to name the field in the database.:
group = models.AutoField(primary_key=True, db_column='group_id')
Documentation
Solution 2
To get the same results in a more "Django" way let Django generate the Primary keys automatically then reference the model in the OneToOne and Foreign key fields as shown below.
class Group(models.Model):
group_name = models.CharField(max_length=50, unique=False, blank=False)
class Group_Cover(models.Model):
group = models.OneToOneField(Group)
group_cover = models.ImageField(upload_to="/image/group/")
class Group_Member(models.Model):
user = models.ForeignKey(User2)
group = models.ForeignKey(Group)
Your assumption is correct, you need to rename your fields to not include the _id (i.e group instead of group_id). This will fix your "issue" but more than anything it more accurately represents the relationship/field. You have relationships to a model, not a reference to the id.
_id is an automatic reference provided by django to make it easier to just retrieve the _id from a model.
From the documentation
Behind the scenes, Django appends "_id" to the field name to create its database column name. In the above example, the database table for the Car model will have a manufacturer_id column. (You can change this explicitly by specifying db_column) However, your code should never have to deal with the database column name, unless you write custom SQL. You’ll always deal with the field names of your model object.
You should not worry about _id that is being added in database table. You should not deal with database if you are using ORM in Django. Also, you do not need to specify id unless its special type - group of attributes.
I would do it like this (I believe you do not need that many classes):
class Group(models.Model):
name = models.CharField(max_length=50, unique=False, blank=False)
cover = models.ImageField(upload_to="/image/group/")
users = models.ManyToManyField(User2)
Then you should access attributes with object notation. If you want id, use group.id, if you want to filter object, use Group.objects.filter(id__gt=10) or Group.objects.get(id=1) etc. My model should be doing exactly what you want to achieve.
Consider the following two Django models:
class Item(models.Model):
'''
Represents a single item.
'''
title = models.TextField()
class Information(models.Model):
'''
Stores information about an item.
'''
approved = models.BooleanField(default=False)
multipurpose_field = models.PositiveIntegerField()
Due to the way the models are organized, I am forced to use a PositiveIntegerField in Information for referencing an Item instead of using a ForeignKey. This makes queries more difficult.
I would like to select all items referenced by an Information instance with approved set to True. In other words, I would like to do this:
Information.objects.filter(approved=True)
...except that the query will return instances of Information instead of the Item referenced in multipurpose_field.
I probably could do this with raw SQL:
SELECT app_item.title FROM app_item
LEFT JOIN app_information
ON app_information.multipurpose_field = app_item.id
WHERE app_information.approved = 1
Is there a way to do this without resorting to raw SQL (which often isn't very portable)?
ForeignKey's field type is determined by the related field. Consider redesigning your models this way:
class Item(models.Model):
id = models.PositiveIntegerField(primary_key=True)
title = models.TextField()
class Information(models.Model):
approved = models.BooleanField(default=False)
multipurpose_field = models.ForeignKey(Item)
Then your query will be represented this way:
Item.objects.filter(information__approved=True)