django_comments adding new field in the model - django

I am trying to add a new field in django_comment model. According to the documentation, most custom comment models will subclass the CommentAbstractModel model:
from django.db import models
from django_comments.models import CommentAbstractModel
class CommentWithTitle(CommentAbstractModel):
title = models.CharField(max_length=300)
If I generate a migration, then it adds all the fields into migrations (all fields from comment model plus title field).
And after running migrations, CommentWithTitle table and django_comments table are created. But django_comments would be useless (not in use).
Another approach is to generate the table this way:
from django_comments.models import Comment
class CommentWithTitle(Comment):
title = models.CharField(max_length=300)
And it generates the migration with one field only with the reference of comment_ptr.
My Question is: which approach is better? I think the first model is good as it has all fields in one table. But that generates the django_model which is not in use at all.

I would follow the documentation.
Looking at the implementation, Comment is basically just extending CommentAbstractModel with db_table specified.
class Comment(CommentAbstractModel):
class Meta(CommentAbstractModel.Meta):
db_table = "django_comments"
I'm suspecting that if you do the second option you mentioned, the migration would throw an error because the db_table will be created twice.

Related

Create dynamic model field in Django?

In my models.py my model store contains name, brand_name fields
Now,I want to create new field called brand_type in store model dynamically from Django admin,how can I do this?
Can we change schema of model using SchemaEditor? How?
you can't create model's fields via admin panel
you must create fields in models.py
admin panel is only a convinient way to manipulate your database, but not modifiyng its tables
It requires migration while the server is running. Create brand_type field under your model and set blank=True
In your models.py
class Store(models.Model):
name = ...
brand_name = ...
brand_type = models.CharField(max_length = 40, blank= True, null =true)
In your console
./manage.py makemigrations
./manage.py migrate
Extra Stuff:
If you are interested in Text Choices
or
If you wanna make it more dynamic based on your use case,
create a model called BrandType and link it in Store using
brand_type = models.ForeignKey(BrandType,on_delete=models.PROTECT)
models.py
class BrandType(models.Model):
name = ...
some_other fields=
#... Store model follows
admin.py
# other stuff
from .models import BrandType
admin.site.register(BrandType)
Take away: It is not advisable to modify your models.py file using admin directly it will cause integrity issues and there are better ways to achieve your desired functionality.

Rename Django's auth_user table?

Stupid question time. In Django, if you want to specify the name of a model's table, you'll do this:
class MyModel(models.Model):
...
class Meta:
db_table = 'mymodel'
Is it possible to change the name of Django's default auth_user table in a similar fashion? I'm always running manual queries against the user table and it would be much faster to type 'user' than 'auth_user'. I've looked at the Django docs and done some Internet searches and I haven't seen anyone address this question.
Thanks!
Both Selcuk and Daniel Roseman have given you good answers. You can extend either one of the classes User or AbstractUser, with the small difference that for the latter you will also need to add the swappable attribute in you Meta class.
Here is an example:
from django.contrib.auth.models import AbstractUser # (or simply User)
class MyUser(AbstractUser):
class Meta:
swappable = 'AUTH_USER_MODEL' # ONLY for AbstractUser
db_table = 'user'
However, pay attention to using 'user' as a table name, because there are databases where this is a reserved word !!!
Good luck.

Import app foreign key model class in another app model

I know there are other similar questions, but I have a specific case:
Right now there are two apps:
-prev_app
-new_app
prev_app contains a "product" model.
new_app contains "store" and "contract" model.
"store" model has a manytomanyfield to "product" model
"contract" is a intermediary model for "store" and "product"
..
.
So now what I want to do is to put that "product" model into the new_app, (so all related models are in one app) but I just don't know how to proceed with the migrations.
Any thoughts?
put db_table to your model and move it wherever you want. it refers then always to that old db table and you dont need any migrations for your new app.
class Product(models.Model):
class Meta:
db_table = 'oldapp_tablename'
just be careful that imports should be adjusted according to new layout.

Django Multiple File Field

Is there a model field that can handle multiple files or multiple images for django? Or is it better to make a ManyToManyField to a separate model containing Images or Files?
I need a solution complete with upload interface in django-admin.
For guys from 2017 and later, there is a special section in Django docs. My personal solution was this (successfully works in admin):
class ProductImageForm(forms.ModelForm):
# this will return only first saved image on save()
image = forms.ImageField(widget=forms.FileInput(attrs={'multiple': True}), required=True)
class Meta:
model = ProductImage
fields = ['image', 'position']
def save(self, *args, **kwargs):
# multiple file upload
# NB: does not respect 'commit' kwarg
file_list = natsorted(self.files.getlist('{}-image'.format(self.prefix)), key=lambda file: file.name)
self.instance.image = file_list[0]
for file in file_list[1:]:
ProductImage.objects.create(
product=self.cleaned_data['product'],
image=file,
position=self.cleaned_data['position'],
)
return super().save(*args, **kwargs)
No there isn't a single field that knows how to store multiple images shipped with Django. Uploaded files are stored as file path strings in the model, so it's essentially a CharField that knows how to be converted to python.
The typical multiple image relationship is built as a separate Image model with an FK pointing to its relevant model, such as ProductImage -> Product.
This setup makes it very easy to add into the django admin as an Inline.
An M2M field would make sense if you it's truly a many to many relationship where say GalleryImages are referenced from 1 or more Gallery objects.
I had to change from having a single file to multiple files in an existing system and after a bit of research ended up using this: https://github.com/bartTC/django-attachments
It should be easy to subclass the model if you want custom methods.
FilerFileField and FilerImageField in one model:
They are subclasses of django.db.models.ForeignKey, so the same rules apply. The only difference is, that there is no need to declare what model we are referencing (it is always filer.models.File for the FilerFileField and filer.models.Image for the FilerImageField).
Simple example models.py:
from django.db import models
from filer.fields.image import FilerImageField
from filer.fields.file import FilerFileField
class Company(models.Model):
name = models.CharField(max_length=255)
logo = FilerImageField(null=True, blank=True)
disclaimer = FilerFileField(null=True, blank=True)
Multiple image file fields on the same model in models.py:
Note: related_name attribute required, it is just like defining a foreign key relationship.
from django.db import models
from filer.fields.image import FilerImageField
class Book(models.Model):
title = models.CharField(max_length=255)
cover = FilerImageField(related_name="book_covers")
back = FilerImageField(related_name="book_backs")
This answer code taken from django-filer document

Adding a "through" table to django field and migrating with South?

Seems like this should be "easy" or at least documented somewhere, I just cant find it.
Lets say I have a model:
class A(models.Model):
users = models.ManyToMany('auth.User', blank=True)
Now I want to migrate to have a through table to add fields to the ManyToMany relation...
class AUsers(models.Model):
user = models.ForeignKey('auth.User')
a = models.ForeignKey('A')
new_field = models.BooleanField()
class A(models.Model):
users = models.ManyToMany('auth.User', blank=True, through='AUsers')
Then I do:
% ./manage.py schemamigration app --auto
Not totally surprising, it tells me it is going to drop the original auto-created through table and create a new one for AUsers. What's the best practice at this point? Is there a decent way to migrate to the new through table? Do I use db_table in Meta? Do I just not use the through=... right away... then do a schemamigration --auto, then a datamigration to copy the current table (somehow, not sure...) and then add the through relation and let it kill the table?
What's the trick here? Is this really that hard?
You should be able to do this pretty easily.
First of all, make sure that the manual through table that you are creating has the same table name in the database as the one Django originally created automatically.
So, first, let's consider a manual through model before your change:
class AUsers(models.Model):
user = models.ForeignKey('auth.User')
a = models.ForeignKey('A')
class Meta:
db_table = 'appname_a_user'
That should be functionally (almost) identical to the ManyToManyField you used to have. Actually, you could make an empty migration and apply it, and then use --auto for your changes (but don't).
Now, add your field like you did in your sample code above, and then run ./manage.py schemamigration appname manual_through_table --empty. That will give you an empty migration named ####_manual_through_table.py.
In the migration itself, there will be a forwards and backwards method. Each one needs to be one line each:
def forwards(self, orm):
db.add_column('appname_a_user', 'new_field', self.gf('django.db.models.fields.BooleanField')(default=False))
def backwards(self, orm):
db.delete_column('appname_a_user', 'new_field')
That should get you what you are after.
If anyone comes across this question when trying to do the same thing with the moderns migration framework, here are the steps:
Create a new model class that exactly matches the built-in through table
Use the Meta class to set the table name to match the existing table
Generate a migration, which will create the new table and set it as the through for the field.
Without running that migration, edit it to wrap it in a migrations. SeparateDatabaseAndState migration, where the auto-generated steps are in the state_operations field and the database operations are empty.
Modify your through table, as required, making sure to generate new migrations as normal.
As mentioned in a comment, the first step may be simplified using db.rename_table as described here, which gives this through model:
class AUsers(models.Model):
user = models.ForeignKey('auth.User')
a = models.ForeignKey('A')
class Meta:
unique_together = (('user', 'a'),)
Then, create a migration with --auto (this way you'll have the names of the DB tables visible), and replace the content with:
class Migration(SchemaMigration):
def forwards(self, orm):
db.rename_table('appname_a_user', 'appname_auser')
def backwards(self, orm):
db.rename_table('appname_auser','appname_a_user')
I just applied it in my project without issues.