Multiple datetime fields for a model in Django - python

I want to create a log system to register some faults I need to handle at my work. I use Django and my models look like these:
class Chan(models.Model):
channelname = models.CharField(max_length=30)
freq = models.FloatField(default = 0.0)
def __unicode__(self):
return u'%s' % (self.channelname)
# timestamp object
class EventTime(models.Model):
since = models.DateTimeField()
till = models.DateTimeField()
def __unicode__(self):
return u'%s' % self.since.strftime('%Y-%m-%d %H:%M')
class Fault(models.Model):
channel = models.ManyToManyField(Chan)
description = models.CharField(max_length=200, default="-")
message = models.TextField()
timeevent = models.ManyToManyField(EventTime,null=True)
visible = models.BooleanField()
Firstly I used just one EventTime object but I soon realized I need to be able to choose several time periods because the same event could happen several times a day. So it would be too tedious to create a new record of Fault each time. So I basically needed something like this:
The problem is that 'ManyToManyField' is too unhandy to use because I don't need to keep these values for other faults. So I don't know what solution I can use for it. I don't know how many time periods I need. Maybe I could add an extra Text field to my table where I would keep comma-separated datetime objects converted into a string like '2017-11-06 18:36,2017-11-06 18:37'. But I don't know where to set this extra-conversion because I want to use a standart DateTimeField in Django admin site to set it before I make this conversion. Or maybe I could change the interface itself and add some Javascript. Maybe someone could give me advice or share some useful links. Thank you.

I would recommend using a Many-to-one relation together with InlineModelAdmin for the django admin.
models.py
class Fault(models.Model):
channel = models.ManyToManyField(Chan)
description = models.CharField(max_length=200, default="-")
message = models.TextField()
visible = models.BooleanField()
class EventTime(models.Model):
since = models.DateTimeField()
till = models.DateTimeField()
fault = models.ForeignKey(Fault, on_delete=models.CASCADE, related_name='timeevents')
def __unicode__(self):
return u'%s' % self.since.strftime('%Y-%m-%d %H:%M')
admin.py
from .models import Fault, EventTime
from django.contrib import admin
class EventTimeInline(admin.TabularInline):
model = EventTime
#admin.register(Fault)
class FaultAdmin(admin.ModelAdmin):
# ...
inlines = [EventTimeInline,]

Related

Django DateTimeField and datetime.datetime.now() giving different times

I have a model where I want the name field to be a string representation of the timestamp, and another field to be the actual time stamp. Here is my model code:
from django.db import models
from datetime import datetime
class Image(models.Model):
name = models.CharField(max_length=255, default=datetime.now().strftime("%Y%m%d-%H%M%S"))
create_date = models.DateTimeField(auto_now_add=True)
image = models.ImageField(upload_to="images/")
Then I go in the django shell and enter this:
>>> import models
>>> models.Image(image='images/rock.png').save()
This works but the only problem is the two times do not align. For example, I get name = 20191201-143119 and create_date = 2019-12-01 14:32:11.445474.
How can I get these two datetimes to be the same?
This is a pretty common gotcha in Django's world. The post mentioned by #eliakin-costa discuss this problem, although his solution works I wouldn't recommend overriding save method to get this behavior as it's easier to create a function (keeping decoupled and explicit):
from django.db import models
from django.utils import timezone
def default_image_name():
return timezone.now().strftime("%Y%m%d-%H%M%S")
class Image(models.Model):
name = models.CharField(max_length=255, default=default_image_name)
create_date = models.DateTimeField(auto_now_add=True)
image = models.ImageField(upload_to="images/")
By the way, did you take a look at this docs (upload_to also accepts a callable) ? Do you really need a name column in your table?
I've linked an answer will help you understand what is happening. Achieving what you want is quite simple though.
models.py
from django.db import models
from datetime import datetime
class Image(models.Model):
name = models.CharField(max_length=255)
create_date = models.DateTimeField(auto_now_add=True)
image = models.ImageField(upload_to="images/")
def save(self, *args, **kwargs):
if not self.name:
self.name = datetime.now().strftime("%Y%m%d-%H%M%S")
super(Image, self).save(*args, **kwargs)

unique_together with a field from a foreign key in a through table for a ManyToMany relation

I am developing a Django 2.0 project app. It has a (non-working) models.py file, which looks something like this:
from django.db import models
from django.utils import timezone
class Computer(models.Model):
name = models.CharField(max_length=25)
def __str__(self):
return "Computer {}".format(self.name)
class Software(models.Model):
name = models.CharField(max_length=25)
description = models.CharField(max_length=1024, blank=True)
def __str__(self):
return self.name
class SoftwareVersion(models.Model):
software = models.ForeignKey(Software, on_delete=models.CASCADE, related_name="versions")
version = models.CharField(max_length=100)
released_at = models.DateTimeField(default=timezone.now)
def __str__(self):
return "{} {}".format(self.software, self.version)
class ComputerSoftwareBundle(models.Model):
computer = models.ForeignKey(Computer, on_delete=models.CASCADE, related_name="bundles")
installed_at = models.DateTimeField(default=timezone.now)
versions = models.ManyToManyField(SoftwareVersion, through="BundleSoftwareVersion", related_name="bundles")
class BundleSoftwareVersion(models.Model):
bundle = models.ForeignKey(ComputerSoftwareBundle, on_delete=models.CASCADE)
version = models.ForeignKey(SoftwareVersion, on_delete=models.CASCADE)
class Meta:
unique_together = (("bundle", "version__software"),)
The app tracks software bundles currently or previously installed on computers. The thing here is that a bundle should not contain more than one version of the same software. Also, SoftwareVersion should contain a reference to Software, because the same version string has a different meaning for different pieces of software.
The code does not work as described in this Stackoverflow answer. I left the unique_together line in to illustrate what I am trying to achieve.
I've tried to work around this limitation of Django (not being able to use fields referred to via a foreign key in unique_together) by overriding the save and validate_unique methods in BundleSoftwareVersion but that did not work out completely well. Here's the implementation I have tried:
class BundleSoftwareVersion(models.Model):
bundle = models.ForeignKey(ComputerSoftwareBundle, on_delete=models.CASCADE)
version = models.ForeignKey(SoftwareVersion, on_delete=models.CASCADE)
def save(self, *args, **kwargs):
self.validate_unique()
super().save(*args, **kwargs)
def validate_unique(self, exclude=None):
super().validate_unique(exclude)
bundle_versions = BundleSoftwareVersion.objects.filter(bundle=self.bundle,
version__software=self.version.software)
count = len(bundle_versions)
if not self.pk:
# if this instance is not stored in the database,
# we need to increment the count to take this instance
# into account
count += 1
if count > 1:
raise ValidationError("There already is an instance of software '{}' in this bundle.".format(self.version.software))
I have thus far tried out these models via the admin site. The checks work when changing an existing ComputerSoftwareBundle (the admin site displays a message next to the offending entry), but adding results in an uncaught exception.
Is there a better way to enforce this kind of uniqueness?
I have come up with a workaround:
class BundleSoftwareVersion(models.Model):
bundle = models.ForeignKey(ComputerSoftwareBundle, on_delete=models.CASCADE)
version = models.ForeignKey(SoftwareVersion, on_delete=models.CASCADE)
_software = models.ForeignKey(Software, on_delete=models.CASCADE, null=True, editable=False)
class Meta:
unique_together = (("bundle", "_software"),)
def save(self, *args, **kwargs):
self._software = self.version.software
super().save(*args, **kwargs)
As you can see, I now have a helper field _software which is used in unique_together and into which the self.version.software is stored on each save.
So far, I have experienced one downside with this approach: trying to save a ComputerSoftwareBundle containing duplicate software instances results in an error page for IntegrityError being displayed instead of an error message within the form.
I would appreciate suggestions on how to fix this downside, or even suggestions for a different approach altogether.

How to authenticate user for a specific object rather than whole class in django?

I am working on making an app to add clubs in website. This is my model.py file
from django.db import models
from stdimage import StdImageField
# Create your models here.
class Club(models.Model):
ClubName = models.CharField(max_length=200)
ClubLogo = StdImageField(upload_to='club_logo', variations={'thumbnail':(150, 200, True)})
ClubDetails = models.TextField()
ClubStartDate = models.DateField()
def __str__(self):
return self.ClubName
class Notice(models.Model):
NOTICE = 'NOTICE'
UPDATES = 'UPDATES'
EVENTS = 'EVENTS'
NOTICE_IN_CHOICES = (
(NOTICE, 'Notice'),
(UPDATES, 'Updates'),
(EVENTS, 'Events'),)
NoticeType = models.CharField(
max_length=20, choices=NOTICE_IN_CHOICES, default=NOTICE)
NoticeTag = models.CharField(max_length=30)
NoticeStartDate = models.DateField(auto_now_add=True)
NoticeEndDate = models.DateField()
NoticeFile = models.FileField(default='#', upload_to='notice/%Y/%m/%d')
NoticeContent = models.TextField(default='NA')
NoticeClub = models.ForeignKey(Club)
def __str__(self):
return self.NoticeTag
class Members(models.Model):
MemeberName = models.CharField(max_length=200)
MemberImage = StdImageField(upload_to='member_photo', variations={'thumbnail':(150, 120, True)})
MemberEmail = models.EmailField()
MemberClub = models.ForeignKey(Club)
def __str__(self):
return self.MemeberName
Now when i am making users via django's inbuilt admin panel i have option to give permission to users to change member of any club but i want to give access to change members of only that particular club which he is member of.
As you can see in this picture that all club are in dropdown option when someone who has access to add notices adding otices. But instead of that i want only one option in the dropdown for the useradmin to which he is associated.
this is my admin.py file
from django.contrib import admin
# Register your models here.
from club.models import Club, Members, Notice
admin.site.register(Club),
admin.site.register(Members),
admin.site.register(Notice),
This is a problem with which many users have been struggling with.
I have been using couple of external packages, and couple of self made solutions. But the best one I have found so far is Django Guardian It's an implementation of per object permission .This means you can manage users and permissions to which they have access to.

Django Admin filter by function / filter only by first object in reverse foreign key lookup

I am trying to build a filter by function in django. From what I've learned by googling this is quite hard to achieve.
So here is my code:
class TrackingEventType(models.Model):
name = models.CharField(blank=False, null=False, max_length=255)
class TrackingEvent(models.Model):
datetime = models.DateTimeField(blank=False, null=False, default=datetime.now, verbose_name="Zeitpunkt")
event_type = models.ForeignKey(TrackingEventType, help_text="Art des Events")
tracking = models.ForeignKey('Tracking')
class Meta:
ordering = ['-datetime']
class Tracking(models.Model):
tracking_no = models.CharField(blank=False, null=False, max_length=10, unique=True, verbose_name="Tracking Nummer")
def get_last_event(self):
"""
Todo: return the latest event.
"""
return TrackingEvent.objects.filter(tracking=self.id).first()
get_last_event.short_description = 'Last event'
class Meta:
ordering = ['-tracking_no']
My goal is to make it possible to filter Tracking objects by their last events type name. Displaying the result of the funtion in django admin is easy, but adding a corresponding filter isn't.
My idea was also to try to build a filter something like:
trackingevent__set__first__event_type__name
But yeah, that would be too easy :)
Any inputs are welcome.
As you've discovered it isn't trivial to filter in that manner. If you are accessing that information regularly it is probably also not very efficient either.
I would suggest that you store a reference to the latest tracking event in the Tracking model itself:
class Tracking(models.Model)
# ...
last_event = models.ForeignKey(TrackingEvent, null=True)
You would then use signals to update this reference whenever a new tracking event is created. Something along the lines of:
from django.db.models.signals import post_save
from django.dispatch import receiver
#receiver(post_save, sender=TrackingEvent)
def update_latest_tracking_event(sender, instance, created, **kwargs):
# Is this a new event?
if created:
# If yes, then update the Tracking reference
tracking = instance.tracking
tracking.last_event = instance
tracking.save()
(Please read the documentation on where to put this code).
Once all this is in place it becomes easy to filter based on the last tracking event type:
# I'm just guess what event types you have...
cancellation = TrackingEventType.objects.get(name='cancel')
Tracking.objects.filter(last_event__event_type=cancellation)

Python Django how to retrieve specific objects

I've got this Post model at the moment:
class Post(models.Model):
title = models.CharField(max_length = 140)
body = models.TextField()
date = models.DateTimeField()
def __unicode__(self):
return self.title
If I've got different parts of a website (or a forum rather) that contain different posts, e.g. Discussion about basketball, and Discussion about football, if I wanted to return just posts concerning basketball or just posts concerning football, is the easiest way to just make a specific basketball_post model/football_post model or is there a more efficient way? Should I perhaps be storing the values differently?
Thanks
Django has a really good tutorial. It is about making a Poll app. In the first chapter the thing you want is discussed. It is about a Question that can have multiple Choices.:
class Question(models.Model):
question_text = models.CharField(max_length=200)
pub_date = models.DateTimeField('date published')
class Choice(models.Model):
question = models.ForeignKey(Question)
choice_text = models.CharField(max_length=200)
votes = models.IntegerField(default=0)
The foreignKey creates a relation between two models. The same can be done for a blog:
class Category(models.Model):
title = models.CharField(max_length=200)
class Post(models.Model):
category = models.ForeignKey(Category) # This is the important part.
title = models.CharField(max_length=200)
body = models.TextField()
date = models.DateTimeField()
def __unicode__(self):
return self.title
The ForeignKey relation lets you do really nice things:
basketball_posts = Post.objects.filter(category_title='Basketball')
But before we all tell you how it is done, I really recommend to do the tutorial. It introduces you to all important Django concepts: https://docs.djangoproject.com/en/1.7/intro/tutorial01/
Update
If you have a fixed set of categories that are not likely to change, than you can hardcode them and use field choices:
class Post(models.Model):
FOOTBALL = 'F' # Variable name and db_value
CRICKET = 'C'
INTRODUCTION = 'I'
CATEGORY_CHOICES = (
(FOOTBALL, 'Soccer'), # Variable name and display value
(CRICKET, 'Cricket'),
(INTRODUCTION, 'Hello my name is'),
)
category = models.CharField(max_length=1,
choices=CATEGORY_CHOICES,
default=INTRODUCTION)
...
https://docs.djangoproject.com/en/dev/ref/models/fields/#choices
One of the advantages of this 'choice machinery' over a CharField without pre defined choices is that you are sure what values end up in your database. This lets you query them, without worrying if your data is sane:
Post.objects.filter(category=Post.CRICKET)
Use the extra table if you need the freedom to create new categories in the future. Use field choices if you don't want (or need) that freedom.
I would suggest to just add a field which makes the post relevant to that certain topic:
class Post(models.Model):
title = models.CharField(max_length = 140)
body = models.TextField()
date = models.DateTimeField()
type = models.CharField(max_length=20) #<--- new field: e.g 'basketball','hockey'..
def __unicode__(self):
return self.title
example query:
#basketball posts
qs = Post.objects.filter(type__icontains="basketball")
then you dont need to have multiple models which also would be redundant.
Assuming all of the posts are in the same format, you could add another field to your model like "type". Different discussion forums could send a different values for that field when the post is added.
type = models.CharField(max_length=140, choices=['Football', 'Basketball', 'Baseball'])
Storing this would make it easy to filter which posts are which.
Post.objects.filter(type = 'Football')
Assuming that one post can be about only one sport, the better approach would be to have a foreign key relation between a model that stores data about a post with another model that stores the data about sports.
Something like this
class Sport(models.Model):
name = models.CharField(max_length = 200)
description = models.TextField()
def __unicode__(self):
return self.name
class Post(models.Model):
title = models.CharField(max_length = 140)
body = models.TextField()
date = models.DateTimeField()
sport = models.ForeignKey(Sport)
def __unicode__(self):
return self.title
This gives you the advantage of isolating the 'Sport' and the 'Post' models.You can add as many sports as you want, without any posts referring to it.
One more advantage is that you can add relevant information to the relevant models.
Eg:Suppose you want to add the information about "how many players are there in a team for sport x?". You can easily achieve this by adding a field "number_of_players" in the 'Sport' model without affecting the 'Post' model.
If you had to do this in one model, 'Post', then it would create lot of issues in terms of data consistency and other undesirable things.
Also, the query will look something like this:
posts = Post.objects.filter(sport__name = "Basketball")
PS:If your requirement is that a post can be tagged to multiple sports, then you can use ManyToMany field instead of a simple foreign key.
https://docs.djangoproject.com/en/dev/topics/db/examples/many_to_many/
You could assign your posts tags or category, and filter on those.
If you use the model approach what happens when you add more sports? You'll need manually add the sports in your code, using a tags or category approach allows you to handle it in the db, and would then allow you to filter on the tags/categories in your system

Categories