In my django template <p>This user is unsubscribed {{sub.is_unsubscribed}}</p> always displays "This user is unsubcribed False" even when it should show True based on the following models.py
from django.shortcuts import get_object_or_404
class Subscriber(models.Model):
email = models.CharField(max_length=12)
first_name = models.CharField(max_length=50)
last_name = models.CharField(max_length=50)
create_date = models.DateTimeField(auto_now_add=True)
def is_unsubscribed(self):
try:
get_object_or_404(MasterUnsubscriber, unsubcriber_email=self.email)
return True
except:
return False
def __str__(self):
return self.email
class MasterUnsubscriber(models.Model):
unsubscriber_email= models.CharField(max_length=12)
And for structural reasons of my app, I do not want to move the unsubscribe to the Subscriber model as a boolean. How can this be resolved while keeping same model formats.
get_object_or_404 will raise an error if there is no element at all, or when there are multiple such elements, since the underlying implementation is to use .get(..). You can make use of .exists() instead:
class Subscriber(models.Model):
# …
def is_unsubscribed(self):
return MasterUnsubscriber.objects.filter(unsubscriber_email=self.mail).exists()
That being said, I would advice to make use a ForeignKey [Django-doc] to the subscriber, not by matching the email address. This will be inefficient, since search on a non-indexed column is slow, and furthermore if the user later changes their email, then the all of a sudden, these can be subscribed again. Django also has convenient ways to filter or make aggregates on related objects, so you get more out of the Django ORM.
Related
I am trying to change the default error message of django admin to my own message app that should display like a normal message system
here is my model for the app
class Role(models.Model):
name = models.CharField(max_length=200)
slug = models.SlugField(null=True, unique=True, blank=False)
def __str__(self):
return self.name
in on other model this model become the forighn key. here is the second model
class LearningPath(models.Model):
Path_Name = models.CharField(max_length=500)
Role = models.ForeignKey(Role, on_delete=models.DO_NOTHING)
after that, I have created some roles and then some Learning Paths.
the issue is that I when I delete any of Role that is used inside any of Learning Path it shows me the below error
what I want is to show the error message in a normal message app as the normal alert div appear. I have tried to write some code in admin.py but it's not working.
here is admin.py code
class RoleAdmin(admin.ModelAdmin):
list_display = ('name',)
prepopulated_fields = {'slug': ('name',)}
list_per_page = 20
def delete_model(self, request, obj):
try:
obj.delete()
except IntegrityError:
messages.error(request, 'This object can not be deleted!')
admin.site.register(Role, RoleAdmin)
Please don't use on_delete=models.DO_NOTHING [Django-doc]. It relies on the fact that the database will somehow deal with it. But that behavior can be unpredictable. Some database can simply ignore this, other might prevent deleting the objects, or fail silently.
It might be better to use PROTECT [Django-doc]. This means that Django will identify the problem itself, and simply prevent querying the database in the first place to remove the object.
Django admin seems to take this into account as well. As we can read in the documentation of the get_deleted_objects(…) method:
(…)
This method must return a 4-tuple of (deleted_objects,
model_count, perms_needed, protected).
(…)
protected is a list of strings representing of all the protected related objects that can’t be deleted. The list is displayed in the template.
thanks to the Willem Van Onsem comment, my issue got fixed.
i have used on_delete=models.PROTECT into my model and it did work what i was looking for
In my model, I have the following M2M field
class FamilyMember(AbstractUser):
...
email_list = models.ManyToManyField('EmailList', verbose_name="Email Lists", blank=True, null=True)
...
The EmailList table looks like this:
class EmailList(models.Model):
name = models.CharField(max_length=50, default='My List')
description = models.TextField(blank=True)
is_active = models.BooleanField(verbose_name="Active")
is_managed_by_user = models.BooleanField(verbose_name="User Managed")
In the app, the user should only see records that is_active=True and is_managed_by_user=True.
In the Admin side, the admin should be able to add a user to any/all of these groups, regardless of the is_active and is_managed_by_user flag.
What happens is that the Admin assigns a user to all of the email list records. Then, the user logs in and can only see a subset of the list (is_active=True and is_managed_by_user=True). This is expected behavior. However, what comes next is not.
The user deselects an email list item and then saves the record. Since M2M_Save first clears all of the m2m records before it calls save() I lose all of the records that the Admin assigned to this user.
How can I keep those? I've tried creating multiple lists and then merging them before the save, I've tried passing the entire list to the template and then hiding the ones where is_managed_by_user=False, and I just can't get anything to work.
What makes this even more tricky for me is that this is all wrapped up in a formset.
How would you go about coding this? What is the right way to do it? Do I filter out the records that the user shouldn't see in my view? If so, how do I merge those missing records before I save any changes that the user makes?
You might want to try setting up a model manager in your models.py to take care of the filtering. You can then call the filter in your views.py like so:
models.py:
class EmailListQuerySet(models.query.QuerySet):
def active(self):
return self.filter(is_active=True)
def managed_by_user(self):
return self.filter(is_managed_by_user=True)
class EmailListManager(models.Manager):
def get_queryset(self):
return EmailListQuerySet(self.model, using=self._db)
def get_active(self):
return self.get_queryset().active()
def get_all(self):
return self.get_queryset().active().managed_by_user()
class EmailList(models.Model):
name = models.CharField(max_length=50, default='My List')
description = models.TextField(blank=True)
is_active = models.BooleanField(verbose_name="Active")
is_managed_by_user = models.BooleanField(verbose_name="User Managed")
objects = EmailListManager()
views.py:
def view(request):
email = EmailList.objects.get_all()
return render(request, 'template.html', {'email': email})
Obviously there is outstanding data incorporated in my example, and you are more than welcome to change the variables/filters according to your needs. However, I hope the above can give you an idea of the possibilities you can try.
In your views you could do email = EmailList.objects.all().is_active().is_managed_by_user(), but the loading time will be longer if you have a lot of objects in your database. The model manager is preferred to save memory. Additionally, it is not reliant on what the user does, so both the admin and user interface have to talk to the model directly (keeping them in sync).
Note: The example above is typed directly into this answer and has not been validated in a text editor. I apologize if there are some syntax or typo errors.
I have the following setup:
class Ride(models.Model):
...
riders = models.ManyToManyField(User, through='RideRiders', null=True, blank=True)
class Meta:
db_table = 'rides'
app_label = 'rides'
class RideRiders(models.Model):
user = models.ForeignKey(User)
ride = models.ForeignKey(Ride)
# Other information
...
class Meta:
db_table = 'rides_riders'
app_label = 'rides'
So, for example, i can get all the rides for a user using the following (is this actually the best way?):
Ride.objects.filter(riders__id=user_id)
I want to be able to get all the users for a particular ride but can't seem to figure out how. This RideRiders model seems to be the sticking point as i can't seem to pass beyond that to the user. For example:
Ride.objects.get(id=ride_id).riders.all()
...gives a list of RideRider objects (i think).
Any suggestions?
EDIT:
To add more context, i'm attempting to fetch the riders for a particular ride as an API call using the Django Rest Framework. This is how i'm using it:
class RideRiders(generics.ListAPIView):
model = User
serializer_class = RiderSerializer
def get_queryset(self):
return Ride.objects.get(id=self.kwargs.get('id')).riders.all()
But this is currently giving me the error which is why i feel it's returning RideRider objects rather than Rider objects.
FieldError: Cannot resolve keyword u'ride' into field. Choices are: date_joined, email, first_name, groups, id, is_active, is_staff, is_superuser, last_login, last_name, logentry, password, user_permissions, username
EDIT 2:
So Ride.objects.get(id=self.kwargs.get('id')).riders.all() is throwing the above error (nothing to do with serializers as thought). If i run Ride.objects.get(id=self.kwargs.get('id')).rideriders_set.all() then it returns a queryset (and testing gives the expected number of records for any particular ride). So, this leaves the original question of how do i return a list of users (rather than RideRider objects)?
The culprit turned out to be app_label = 'rides' in the model classes was incorrect after some refactoring of the overall application. This was clearly stopping Django from making the proper lookups across models as they weren't being registered properly.
I'm trying to use a runtime-computed field in my admin page. This works fine, but I'd like to allow sorting based for that field. Using Django 1.5 (dev), is this possible? I've been scouring the interweb but can't find anything indicating that it is possible.
class Guest(models.Model)
email = models.CharField(max_length=255)
class Invitation(models.Model)
guest = models.ForeignKey(Guest)
created_on = models.DateTimeField(auto_now_add=True)
class GuestAdmin(admin.ModelAdmin):
list_display = ["email", "latest_invitation_sent_on",]
def latest_invitation_sent_on(self, o):
try:
return o.invitation_set.all().order_by(
"-created_on")[0].created_on.strftime("%B %d, %Y")
except IndexError:
return "N/A"
I'd like to be able to enable sorting by latest_invitation_sent_on. Are there any methods of doing this nicely that I'm unaware of?
You should be able to annotate Guests with their latest invitation time and then order_by it (order_by uses the DB to sort and as long as you can provide a valid DB field, table or virtual it should work).
class GuestManager(models.Manager):
def get_query_set(self):
return super(GuestManager, self).get_query_set().annotate(latest_invite=Max("invitation_set__created_on"))
class Guest(models.Model)
email = models.CharField(max_length=255)
objects = GuestManager()
class Invitation(models.Model)
guest = models.ForeignKey(Guest)
created_on = models.DateTimeField(auto_now_add=True)
class GuestAdmin(admin.ModelAdmin):
list_display = ["email", "latest_invite",]
If you only need latest_invite annotation once in a while it makes sense to move it to a separate method or even manager.
class GuestManager(models.Manager):
def by_invitations(self):
return super(GuestManager, self).get_query_set().annotate(latest_invite=Max("invitation_set__created_on")).order_by('-latest_invite')
>>> Guest.objects.by_invitations()
I'm doing something that doesn't feel very efficient. From my code below, you can probably see that I'm trying to allow for multiple profiles of different types attached to my custom user object (Person). One of those profiles will be considered a default and should have an accessor from the Person class. Storing an is_default field on the profile doesn't seem like it would be the best way to keep track of a default, is it?
from django.db import models
from django.contrib.auth.models import User, UserManager
class Person(User):
public_name = models.CharField(max_length=24, default="Mr. T")
objects = UserManager()
def save(self):
self.set_password(self.password)
super(Person, self).save()
def _getDefaultProfile(self):
def_teacher = self.teacher_set.filter(default=True)
if def_teacher: return def_teacher[0]
def_student = self.student_set.filter(default=True)
if def_student: return def_student[0]
def_parent = self.parent_set.filter(default=True)
if def_parent: return def_parent[0]
return False
profile = property(_getDefaultProfile)
def _getProfiles(self):
# Inefficient use of QuerySet here. Tolerated because the QuerySets should be very small.
profiles = []
if self.teacher_set.count(): profiles.append(list(self.teacher_set.all()))
if self.student_set.count(): profiles.append(list(self.student_set.all()))
if self.parent_set.count(): profiles.append(list(self.parent_set.all()))
return profiles
profiles = property(_getProfiles)
class BaseProfile(models.Model):
person = models.ForeignKey(Person)
is_default = models.BooleanField(default=False)
class Meta:
abstract = True
class Teacher(BaseProfile):
user_type = models.CharField(max_length=7, default="teacher")
class Student(BaseProfile):
user_type = models.CharField(max_length=7, default="student")
class Parent(BaseProfile):
user_type = models.CharField(max_length=7, default="parent")
First of all you could make things a lot more easy by not declaring the BaseProfile abstract:
from django.db import models
from django.contrib.auth.models import User, UserManager
class Person(User):
public_name = models.CharField(max_length=24, default="Mr. T")
objects = UserManager()
def save(self):
self.set_password(self.password)
super(Person, self).save()
def _getDefaultProfile(self):
try:
return self.baseprofile_set.get(default=True)
except ObjectDoesNotExist:
return False
profile = property(_getDefaultProfile)
def _getProfiles(self):
return self.baseprofile_set.all()
profiles = property(_getProfiles)
class BaseProfile(models.Model):
person = models.ForeignKey(Person)
is_default = models.BooleanField(default=False)
class Teacher(BaseProfile):
user_type = models.CharField(max_length=7, default="teacher")
class Student(BaseProfile):
user_type = models.CharField(max_length=7, default="student")
class Parent(BaseProfile):
user_type = models.CharField(max_length=7, default="parent")
The way this is nicer? Your properties didn't know anyway what type they were returning, so the abstract baseclass only made you have an incredible annoying overhead there.
If you now are wondering how the hell you can get the data from the specific profiles since I made anything returned BaseProfile? You can do something like this:
try:
#note the lowercase teacher referal
print myuser.profile.teacher.someteacherfield
except Teacher.DoesNotExist:
print "this is not a teacher object!"
Also I do hope you didn't use the user_type field solely for this purpose, because django has it built in better as you can see. I also hope you really have some other unique fields in your derived profile classes because otherwise you should throw them away and just past a usertype field into BaseProfile (look at choices to do this good).
Now as for the is_default, imho this method is as good as any. You can always try to add custom constraints to your dbms itself, saying there sould be 0 or 1 records containing the same FK and is_default=True (there is no django way to do this). What I also would say is, add a method make_default and in that method make sure the is_default is unique for that person (e.g. by first setting is_default to False on all profiles with the same FK). This will save you a lot of possible sorrow. You can also add this check in the save() method of BaseProfile.
Another way you could do it is by adding a Foreign Key to the Person Model that points to the default Profile. While this will ensure default to be unique on django level, it can also provide denormalization and corruption of your data, even on a more annoying level, so I'm no big fan of it. But again, if you do all adding/removing/updating of profiles through predefined methods (will be more complex now!) you should be safe.
Finally, maybe you have good reasons to inherit from User, but the default way to extend the User functionality is not this, it's described here.