dynamically add instances of a model in Django forms - django

I have the following model:
from django.db import models
from django.contrib.auth import get_user_model
User = get_user_model()
class CalEvents(models.Model):
user = models.ForeignKey(User)
activity = models.CharField(max_length=256, unique=False, blank=True)
activity_type = models.CharField(max_length=30, unique=False, blank=True)
activity_code = models.CharField(max_length=30, unique=False, blank=True)
def __str__(self):
return "Activity, type and code for the calendar events of #{}".format(self.user.username)
What I would like to know is how can I dynamically add instances of that model with forms. I'm imagining something like having a form for the first instance (with fields "activity", "activity_type" and "activity_code") and then, if the user clicks a "plus sign" (or whatever), (s)he can add a second, third...Nth instance to the database.

You can achieve this functionality using Django's inline formsets.
https://docs.djangoproject.com/en/1.11/topics/forms/modelforms/#inline-formsets
It will allow you to declare a form which can be used dynamically to add multiple instances of a model.

Related

Why does Django not find a field added to the AbstractBaseUser

I've inherited from the AbstractBaseUser as follows:
class User(AbstractBaseUser):
"""
Main User model, inherits from AbstractBaseUser
"""
# Meta
email = models.EmailField(verbose_name='email', max_length=60, unique=True)
username = models.CharField(max_length=40, unique=True) # equals to email
date_joined = models.DateTimeField(verbose_name='date joined', auto_now_add=True)
last_login = models.DateTimeField(verbose_name='last login', auto_now=True)
employee_of = models.OneToOneField(Customer, on_delete=models.SET_NULL, null=True)
So each User is linked to one and only one Customer.
Now within a view I want to access the instance of the current logged in user within the request object and get the employee_of value to get a queryset that contains all users of that customer.
def render_employees(request):
"""
Renders the employees page of the dashboard
:param request:
:return:
"""
# Return the value for the current site for css specific classes
dashboard_site = 'employees'
# Query the employees
qs_employees = User.objects.filter(employee_of=request.user.employee_of) # doesn't find field
...
However the filter doesn't work because request.user.employ_of doesn't seem to return anything. My IDE even suggests e.g. username, date_joined etc. but not employee_of.
Why's that?
class Customer(models.Model):
"""
A table that stores static data about a customer, usually a legal company
"""
legal_name = models.CharField(max_length=50)
street = models.CharField(max_length=30)
street_number = models.CharField(max_length=3)
def __str__(self):
return self.legal_name
Update:
from django.db import models
from django.contrib.auth.models import AbstractBaseUser, BaseUserManager
from applications.customer.models import Customer
from django.conf import settings
BaseUser = settings.AUTH_USER_MODEL
class User(AbstractBaseUser):
"""
Main User model, inherits from AbstractBaseUser
"""
# Relations
user = models.OneToOneField(BaseUser, related_name='user_profile', on_delete=models.CASCADE, null=True) # link to default user model
employee_of = models.OneToOneField(Customer, on_delete=models.SET_NULL, null=True, blank=True)
I linked the user to the default user model via Django admin. However in the view im still not able to access employee_of within request.user
It seems that request.user is a different model. It's User model from django.contrib.auth. https://docs.djangoproject.com/en/4.0/ref/contrib/auth/#django.contrib.auth.models.User.
What you can do about it?
In our app we have UserProfile model that have OnetoOne relation to django User.
You can then store employee_of value there.
class UserProfile(AbstractBaseUser):
user = models.OnetoOneField("auth.User", related_name="user_profile", on_delete=models.CASCADE)
employee_of = models.OneToOneField(Customer, on_delete=models.SET_NULL, null=True)
and then access request.user employees using something like
request.user.user_profile.employee_of

Django - How to only allow staff users to see their own posts in admin panel

I have a model called listings and I want staff users to only be able to view, edit, delete listings in the admin panel that they created. Currently staff users can view edit and delete all of the listings
here is my listings/admin.py
from django.contrib import admin
from .models import Listing
class ListingAdmin(admin.ModelAdmin):
list_display =('id','Full_Name','Is_Published','Town_Or_City','District','Region','List_Date')
list_display_links = ('id','Full_Name')
list_editable = ('Is_Published',)
search_fields = ('Full_Name','Town_Or_City','District','Region',)
admin.site.register(Listing, ListingAdmin)
here is my listings/models.py
from django.db import models
from datetime import datetime
from FuneralHomes.models import FuneralHome
class Listing(models.Model):
index = models.ForeignKey(index, on_delete=models.DO_NOTHING,blank=True)
Full_Name = models.CharField(max_length=200)
First_Name = models.CharField(max_length=200)
Last_Name = models.CharField(max_length=200)
Nee = models.CharField(max_length=200, blank=True)
Town_Or_City = models.CharField(max_length=200)
District = models.CharField(max_length=200, blank=True)
Region = models.CharField(max_length=200)
List_Date = models.DateField(max_length=200)
Photo = models.ImageField(upload_to='photos/%Y/%m/%d', blank=True)
Is_Published = models.BooleanField(default=True)
List_Date = models.DateTimeField(default=datetime.now, blank=True)
def __str__(self):
return self.Full_Name
The admin panel really isn't the place for things like this (as explained in the first paragraph of the Django documentation).
A quick a dirty way of accomplishing what you're trying to do is probably overriding the delete method for this model to check if the user created it. For only listing the user's posts you could utilize the Manager class. Finally, to handle editing, you would have to override the save method to see if it already exists and if the user created it.
you can override get_queryset function and just filter listings related to that user , in this case user can only see his listings .
class ListingAdmin(admin.ModelAdmin):
def get_queryset (self, request):
return Listing.objects.filter(listing_user = request.user)

Django 1.11 many to many does not appear in django admin

Hi i have a django model for notification which have a many-to-many relation but nothing appears in django admin ( all fields do not appear)
class Notification(models.Model):
"""send notification model"""
title = models.CharField(max_length=200)
text = models.TextField(null=True, blank=True)
device = models.ManyToManyField(Device, null=True, blank=True)
country = models.ManyToManyField(Country, null=True, blank=True)
sent = models.BooleanField(default=False)
when i open django admin for this model and press add notification this is what happens (nothing appears)
Country and Device Code
class Device(models.Model):
"""Store device related to :model:`accounts.User`."""
user = models.OneToOneField(User, related_name='device', on_delete=models.CASCADE)
model = models.CharField(max_length=200, blank=True, null=True)
player_id = models.CharField(max_length=200, blank=True, null=True)
class Meta:
verbose_name = 'Device'
verbose_name_plural = 'Devices'
def __str__(self):
return self.model
class Country(models.Model):
name = models.CharField(max_length=255)
def __str__(self):
return self.name
Admin.py
from django.contrib import admin
from .models import Notification
admin.site.register(Notification)
Edit:
Thank you all the problem is solved
The problem was caused by some entries in device model that did have None in the model field so there was a problem displaying it correctly.
According to https://code.djangoproject.com/ticket/2169 :
When a class has a field that isn't shown in the admin interface but
must not be blank, it's impossible to add a new one. You get a cryptic
"Please correct the error below." message with no shown errors. The
error message should probably say something about a hidden field.
Now ManyToManyFields don't need null=True, try removing those statements and see if you get an improvement.
Also, try adding the Country and Device models in admin.py so admin can see them and display them.
https://docs.djangoproject.com/en/1.11/ref/contrib/admin/#working-with-many-to-many-models
Define an inline for the many-to-manys in admin.py:
from django.contrib import admin
class DeviceInline(admin.TabularInline):
model = Notification.device.through
class CountryInline(admin.TabularInline):
model = Notification.country.through
class NotificationAdmin(admin.ModelAdmin):
inlines = [
DeviceInline, CountryInline
]
exclude = ("device", "country")

AttributeError: 'Manager' object has no attribute 'get_by_natural_key'

I'm using Django 1.5 on Python 3.2.3.
When I run python3 manage.py syncdb, it builds the DB tables, & asks for my email (defined as the unique instead of a username), and then when I enter that, I get this error --
AttributeError: 'Manager' object has no attribute 'get_by_natural_key'
Oddly, it creates the tables anyway, but now, I'm confused 'cause I really don't get what I'm supposed to do. The documentation says I should create a Custom User Manager, but that's all it really says. It doesn't give me a clue where to create it or how. I looked through the docs on Managers, but that really didn't help me figure anything out. It's all too vague. I keep Googling trying to find some clue as to what I need to do, but I'm just going crazy with more and more questions instead of any answer. Here's my models.py file:
from django.db import models
from django.contrib.auth.models import AbstractBaseUser
class MyUsr(AbstractBaseUser):
email = models.EmailField(unique=True,db_index=True)
fname = models.CharField(max_length=255,blank=True, null=True)
surname = models.CharField(max_length=255,blank=True, null=True)
pwd_try_count = models.PositiveSmallIntegerField(blank=True, null=True)
pwd_last_try = models.DateTimeField(blank=True, null=True)
resetid = models.CharField(max_length=100,blank=True, null=True)
last_reset = models.DateTimeField(blank=True, null=True)
activation_code = models.CharField(max_length=100,blank=True, null=True)
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = ['fname','activation_code']
How do I write a Custom User Manager? Do I put it in the MyUsr model as a method? Or is that even what I'm supposed to do? Should I be doing something totally different? I'm not sure of anything at this point. I just don't understand. The documentation on this doesn't seem clear to me, leaving a lot open for interpretation. I'm pretty new to Django, but I've been into Python for several months.
You define a custom manager by sub-classing BaseUserManager and assigning it in your Model to the objects attribute.
from django.contrib.auth.models import AbstractUser, BaseUserManager
class MyMgr(BaseUserManager):
def create_user(...):
...
def create_superuser(...):
...
class MyUsr(AbstractBaseUser):
objects = MyMgr()
email = models.EmailField(unique=True, db_index=True)
fname = models.CharField(max_length=255, blank=True, null=True)
...
You must define the create_user and create_superuser methods for your BaseUserManager. See the docs.
Depending on your case all you might need to fix this error will be to assign the objects variable in the CustomUser class, to inherit form BaseUserManager() instead of models.Manager() which some would have initially set
from django.db import models
from django.contrib.auth.models import AbstractUser, BaseUserManager
class CustomUser(AbstractUser):
name = models.CharField(null=True, blank=True, max_length=100)
......
# model managers
# objects = models.Manager()
objects = BaseUserManager()

Django models with OneToOne relationships?

Let's say I'm using the default auth.models.User plus my custom Profile and Address models which look like this:
class Profile(models.Model):
user = models.OneToOneField(User)
primary_phone = models.CharField(max_length=20)
address = models.ForeignKey("Address")
class Address(models.Model):
country = CountryField(default='CA')
province = CAProvinceField(default='BC')
city = models.CharField(max_length=80)
postal_code = models.CharField(max_length=6)
street1 = models.CharField(max_length=80)
street2 = models.CharField(max_length=80, blank=True, null=True)
street3 = models.CharField(max_length=80, blank=True, null=True)
Now I want to create a registration form. I could create a ModelForm based on User but that won't include fields for the Profile and Address (which are required). So what's the best way to go about building this form? Should I even use ModelForm at all?
Furthermore, how would I use the same form for editing the complex object? I could easily pass an instance of Profile back to it, which holds references to the necessary Address and Profile objects, but how do I get it to fill in the fields for me?
What about using 3 separate ModelForm. One for Address, one for User, and one for Profile but with :
class ProfileForm(ModelForm):
class Meta:
model = Profile
exclude = ('user', 'address',)
Then, process these 3 forms separately in your views. Specifically, for the ProfileForm use save with commit=False to update user and address field on the instance :
# ...
profile_form = ProfileForm(request.POST)
if profile_form.is_valid():
profile = profile_form.save(commit=False)
# `user` and `address` have been created previously
# by saving the other forms
profile.user = user
profile.address = address
Don't hesitate to use transactions here to be sure rows get inserted only when the 3 forms are valid.
You should look into the officially recommended way to extend the User model first, as seen in the docs, which I believe comes directly from the project manager's personal blog about the subject. (The actual blog article is rather old, now)
As for your actual issue with forms, have a look at the project manager's own reusable django-profiles app and see if perusing the code solves your issue. Specifically these functions and the views in which they are utilized.
Edited to Add:
I've looked into it a bit (as I needed to do so myself). It seems something like so would be sufficient:
# apps.profiles.models
from django.db import models
from django.contrib.auth.models import User
class UserProfile(models.Model):
user = models.ForeignKey(User, unique=True)
...
birth_date = models.DateField(blank=True, null=True)
joined = models.DateTimeField(auto_now_add=True)
modified = models.DateTimeField(auto_now=True)
class Meta:
verbose_name = 'user profile'
verbose_name_plural = 'user profiles'
db_table = 'user_profiles'
class Address(models.Model):
user = models.ForeignKey(UserProfile)
...
# apps.profiles.forms
from django import forms
from django.forms import ModelForm
from django.forms.models import inlineformset_factory
from django.contrib.auth.models import User
from apps.profiles.models import UserProfile, Address
class UserForm(ModelForm):
class Meta:
model = User
...
class UserProfileForm(ModelForm):
class Meta:
model = UserProfile
...
AddressFormSet = inlineformset_factory(UserProfile, Address)
I was using "..." to snip content in the code above. I have not yet tested this out but from looking through examples and the documentation on forms I believe this to be correct.
Note I put the FK from the Address model to the UserProfile and not the other way around, as in your question. I believe the inline formsets need this to work correctly.
Then of course in your views and templates you will end up treating UserForm, UserProfileForm, and AddressFormSet separately but they can all be inserted into the same form.
I think your are looking for inline formsets with model forms. This helps you to deal with multiple forms on one page and also takes care of foreign key relations.
Update:
Maybe this question helps you too: Django: multiple models in one template using forms