populating a tuple with model objects - django

I have a simple checklist in my personal info form that users can fill. this checklist gets its choices from tuple and that tuple gets its items from another model called field like this:
class Field(models.Model):
id = models.AutoField(primary_key=True)
slug = models.CharField(max_length=16, default='default')
title = CharField(max_length=32)
INTERESTS = (Field.objects.values_list('slug', 'title'))
everything works just fine. however, when I add a new field object, INTERESTS tuple wont get updated without migrations. how can I update my tuple without any migrations? is it even possible?
this is my simplified models:
class PersonalInfo(models.Model):
interests = MultiSelectField(choices=INTERESTS, blank=True)
and my form:
class Interests(forms.ModelForm):
interests = forms.MultipleChoiceField(widget=forms.CheckboxSelectMultiple, choices=INTERESTS)
class Meta:
model = PersonalInfo
fields = ['interests']

Not an actual answer, but it's too long for a comment. I dont know how much you "simplified the model", but you should change the interest into a ManyToManyField. Right now if any value will be deleted from Field, you will get invalid data in the database. If you will use ManyToManyField, it will it will make sure your DB data consistent will be consistent. So your model will look like this:
class PersonalInfo(models.Model):
interests = models.ManyToManyField(Field)
After doing this, using the ModelForm should handle the data in form for you without doing anything "manually" there.

Related

ReverseManyToOne create object from children through django form

Using:
Python 3.7.3
django 2.2.5
mysql 5.7.27
I have the following models:
class Item(models.Model):
...
class Comments(models.Model):
Item = models.ForeignKey('Item', default=None, null=True, on_delete=models.CASCADE)
Comment = models.TextField(max_length=512, default="", blank=True)
I would like to create a Comments object, when creating the Item through a django form. I tried to do:
class ItemInsertForm(forms.ModelForm):
...
Comments = forms.CharField(required=False,
widget=forms.Textarea(attrs={'placeholder':"Use comments to describe item details \
or issues that other users should know",
'rows':5,
'cols':50,
}
)
)
def clean_Comments(self, *args, **kwargs):
_comment = self.cleaned_data.get('Comments')
_comments = Item.comments_set.create(Comment=_comment)
return _comments
but I get the following error:
'ReverseManyToOneDescriptor' object has no attribute 'create'
Both tables are empty, so no Item and no Comments currently exist. I guess that's why there is no 'create' method available. Is there a way I could achieve what I want?
Or is there another way to manage the comments for the Item object? I created another table to be able to associate multiple comments to the same item and differentiate between them. A character field in the Item class would concatenate all comments in a single string.
I see quite some issues with your code, but since I feel like you gave it an honest shot, I'll try to help you out as best as I can.
First of all your models.py file:
Model names should be singular, so instead of Comments, use Comment.
Class members should be lowercase, so Item and Comment should be changed to item and comment.
Comment.comment is still not very descriptive. The comment is the actual object, it's content is the text within the comment, so text would be more appropriate here.
A ForeignKey with null=True already sets default to None.
Taking this into account and cleaning up your models.py:
class Item(models.Model):
...
class Comment(models.Model):
item = models.ForeignKey(Item, null=True, on_delete=models.CASCADE)
text = models.TextField(max_length=512, default="", blank=True)
Then, moving on to your form:
Since it's a form for creation Comments, a more appropriate name would be CommentForm.
def clean_Comments(self, *args, **kwargs): is a function reserved for doing validation on the Comments field, not for creating an object from the form input. For that you can use the ModelForm's save() method. You only need to define a save method if you're going to perform some custom logic though.
Let's fix those issues first, before I move onto the error message your getting:
class ItemInsertForm(forms.ModelForm):
class Meta:
model = Comment
fields = ['text']
text = forms.CharField(required=False,
widget=forms.Textarea(attrs={'placeholder':"Write your comment to describe item details \
or issues that other users should know",
'rows':5,
'cols':50,
}
)
)
This form, when submitted, will create a Comment object. However, there is still no ability to add the comment to an Item.
To do this, you need to make sure there are Item instances in the database, or allow the user to create one through an ItemForm
There are multiple ways to do this:
Add a ModelChoiceField to the CommentForm, which will allow the user to select an item from a select.
item = forms.ModelChoiceField(queryset=Item.object.all(),
to_field_name = '<item_name>',
empty_label="Select an Item")
When you want to add this form to an something like an ItemDetailPage, you can use the currently viewed Item using something like
item = Item.objects.get(pk=<item_id>)
or
item = Item.objects.create(<item_properties_here>)
then, when saving your form:
comment = form.save()
comment.item = item.
comment.save()
The third way is what you were trying, and why you were getting an error. Retrieve an item, then add the comment saved from the form to the item.comment_set.
Something like this:
item = Item.objects.get(pk=<item_id>)
comment = form.save()
item.comments_set.add(comment)
item.save()

In Django admin, in a Many-to-One relationship, show a select list to pick EXISTING "Many" from the "One"

Imagine we have a model like that:
class Container(models.Model):
name = models.CharField(max_length=60)
class Element(models.Model):
container = models.ForeignKey(Container, blank=True, null=True)
Container is the One, Element is the many.
In Django admin, if I add a StackedInline with model=Element to the inlines of the Container model admin:
class Inline(admin.StackedInline):
model = Element
class ContainerAdmin(admin.ModelAdmin):
inlines = (Inline,)
admin.site.register(Container, ContainerAdmin)
I end up with a formset allowing me to enter new Element objects on the Add Container form.
Instead, I would like to be given a select widget, to pick existing Element objects.
Is that possible without introducing an extra model ?
I think you should be able to do it like this:
class ContainerAdminForm(forms.ModelForm):
class Meta:
model = Container
fields = ('name',)
element_set = forms.ModelMultipleChoiceField(queryset=Element.objects.all())
class ContainerAdmin(admin.ModelAdmin):
form = ContainerAdminForm
# register and whatnot
I don't know that I have anything like this in my project, but I'll let you know if I find something. You may also have to override the save() method on the form in order to actually save the selected Elements; I don't know if naming the field element_set (or whatever the name of the reverse relation is) will be enough.

Django Rest framework serializer exclude foreign key with depth 2

I have made an api that returns an object as json data. I Am using the django-rest-framework and its serializer. Using the resources (ModelResource) I excluded some fields, like a property called 'owner'. One of the fields is a foreignkey to itselve. I want to show this field in the api (so I use depth=2), but I want to exclude the same fields as I excluded in the object returning.
Is there a nice way to do this (I have tried several things without the wanted result).
This is my (simplified) code:
in models.py:
class MyObject(models.Model):
name = models.CharField(max_length=256, blank=True)
parent = models.ForeignKey('self', blank=True, null=True, default=None)
and_some_otherfields = models.otherFields(....)
owner = models.ForeignKey(User, null=True, blank=True, related_name='myobject_owner')
in resource.py:
class MyObjectResource(ModelResource):
model = MyObject
exclude = ('owner','and some other fields',)
and in the view used to return the object it returns this:
data = Serializer(depth=2).serialize(my_object)
return Response(status.HTTP_200_OK, data)
In the response it leaves out the exclude fields (as I wanted and expected).
but in the field parent, the parent myobject with all fields I want to hide.
I Am looking for a way to indicate that for this parent object, the serializer should use the same Resource, or add the secundary fields to the exclude list....
If I use depth =1 it only shows whether it has a parent ([]), or null if not, and i do need to know at least the parent's ID.
Ah, i just found it:
I need to add in the resource for all fields I want to show what resource....
fields = ('name', ("parent","MyObjectResource") , 'and all the other fields you want to see as well...')
I found it here: google groups forum question
You can skip the exlude, it ignores it, and just add the fields you want to show, you do not have to define them, unless you need to indicate what resource to use.
So following is the final code of the resource.py part:
class MyObjectResource(ModelResource):
model = MyObject
fields = ('name', ("parent","MyObjectResource"), 'and all the other fields you want to see as well...')
Here is how an other solution could be.
class ProToPicturesSerial(serializers.ModelSerializer):
pro_pictures = PictureSerializer(many=True)
pro_videos = VideoSerializer(many=True)
city_pro = CitySerializer(many=True)
class Meta:
model = Province
fields = ('id', 'name', 'intro', 'description', 'display_url', 'pro_pictures', 'pro_videos', 'city_pro')

Django forms with odd model relationship

I am working with an existing database that I can not modify and having some trouble trying to deal with presenting forms for modifying the database in Django. The structure in question is as follows and all models are unmanaged.
class Persons(models.Model):
personid = models.BigIntegerField(primary_key=True, db_column='PersonID')
....
class Phones(models.Model):
phoneid = models.BigIntegerField(primary_key=True, db_column='PhoneID')
number = models.CharField(max_length=60, db_column='Number', blank=True)
type = models.CharField(max_length=15, db_column='Type', blank=True)
...
class Personsphones(models.Model):
personphoneid = models.BigIntegerField(primary_key=True, db_column='PersonPhoneID')
personid = models.ForeignKey(Persons, db_column='PersonID')
phoneid = models.ForeignKey(Phones, db_column='PhoneID')
...
I want to create a form to display all of the 'Phones' associated with a particular 'Persons' and in addition be able to modify/add/remove 'Phones' belonging to a 'Persons'. Right now the only thing I can think of is to display the 'Phones' in a modelformset and then if one is added or removed manually set the 'Personsphones' relation. Any ideas on how to best deal with this model setup?
For making changes to your models you may want to use django-south http://south.aeracode.org/docs/
As far as displaying your 'Phone' under your forms.py you may want to set up class meta like so. With this any changes made to models will reflect on change
class Meta:
model = Persons
exclude = ('user')
In models you may want to use Foreignkey fore relationships between phones and Persons. Better seen in action here https://docs.djangoproject.com/en/dev/ref/models/fields/#foreignkey

How to create a unique_for_field slug in Django?

Django has a unique_for_date property you can set when adding a SlugField to your model. This causes the slug to be unique only for the Date of the field you specify:
class Example(models.Model):
title = models.CharField()
slug = models.SlugField(unique_for_date='publish')
publish = models.DateTimeField()
What would be the best way to achieve the same kind of functionality for a non-DateTime field like a ForeignKey? Ideally, I want to do something like this:
class Example(models.Model):
title = models.CharField()
slug = models.SlugField(unique_for='category')
category = models.ForeignKey(Category)
This way I could create the following urls:
/example/category-one/slug
/example/category-two/slug
/example/category-two/slug <--Rejected as duplicate
My ideas so far:
Add a unique index for the slug and categoryid to the table. This requires code outside of Django. And would the built-in admin handle this correctly when the insert/update fails?
Override the save for the model and add my own validation, throwing an error if a duplicate exists. I know this will work but it doesn't seem very DRY.
Create a new slug field inheriting from the base and add the unique_for functionality there. This seems like the best way but I looked through the core's unique_for_date code and it didn't seem very intuitive to extend it.
Any ideas, suggestions or opinions on the best way to do this?
What about unique_together?
class Example(models.Model):
title = models.CharField()
slug = models.SlugField(db_index=False)
category = models.ForeignKey(Category)
class Meta:
unique_together = (('slug','category'),)
# or also working since Django 1.0:
# unique_together = ('slug','category',)
This creates an index, but it is not outside of Django ;) Or did I miss the point?