mirror of
https://github.com/netbox-community/netbox.git
synced 2025-07-14 01:41:22 -06:00
Closes #1283: Added a time zone field to the site model
This commit is contained in:
parent
9984238f2a
commit
b20258c66e
@ -20,7 +20,7 @@ from extras.api.customfields import CustomFieldModelSerializer
|
||||
from ipam.models import IPAddress, VLAN
|
||||
from tenancy.api.serializers import NestedTenantSerializer
|
||||
from users.api.serializers import NestedUserSerializer
|
||||
from utilities.api import ChoiceFieldSerializer, ValidatedModelSerializer
|
||||
from utilities.api import ChoiceFieldSerializer, TimeZoneField, ValidatedModelSerializer
|
||||
from virtualization.models import Cluster
|
||||
|
||||
|
||||
@ -58,13 +58,14 @@ class WritableRegionSerializer(ValidatedModelSerializer):
|
||||
class SiteSerializer(CustomFieldModelSerializer):
|
||||
region = NestedRegionSerializer()
|
||||
tenant = NestedTenantSerializer()
|
||||
time_zone = TimeZoneField(required=False)
|
||||
|
||||
class Meta:
|
||||
model = Site
|
||||
fields = [
|
||||
'id', 'name', 'slug', 'region', 'tenant', 'facility', 'asn', 'physical_address', 'shipping_address',
|
||||
'contact_name', 'contact_phone', 'contact_email', 'comments', 'custom_fields', 'count_prefixes',
|
||||
'count_vlans', 'count_racks', 'count_devices', 'count_circuits',
|
||||
'id', 'name', 'slug', 'region', 'tenant', 'facility', 'asn', 'time_zone', 'physical_address',
|
||||
'shipping_address', 'contact_name', 'contact_phone', 'contact_email', 'comments', 'custom_fields',
|
||||
'count_prefixes', 'count_vlans', 'count_racks', 'count_devices', 'count_circuits',
|
||||
]
|
||||
|
||||
|
||||
@ -77,12 +78,13 @@ class NestedSiteSerializer(serializers.ModelSerializer):
|
||||
|
||||
|
||||
class WritableSiteSerializer(CustomFieldModelSerializer):
|
||||
time_zone = TimeZoneField(required=False)
|
||||
|
||||
class Meta:
|
||||
model = Site
|
||||
fields = [
|
||||
'id', 'name', 'slug', 'region', 'tenant', 'facility', 'asn', 'physical_address', 'shipping_address',
|
||||
'contact_name', 'contact_phone', 'contact_email', 'comments', 'custom_fields',
|
||||
'id', 'name', 'slug', 'region', 'tenant', 'facility', 'asn', 'time_zone', 'physical_address',
|
||||
'shipping_address', 'contact_name', 'contact_phone', 'contact_email', 'comments', 'custom_fields',
|
||||
]
|
||||
|
||||
|
||||
|
@ -7,6 +7,7 @@ from django.contrib.auth.models import User
|
||||
from django.contrib.postgres.forms.array import SimpleArrayField
|
||||
from django.db.models import Count, Q
|
||||
from mptt.forms import TreeNodeChoiceField
|
||||
from timezone_field import TimeZoneFormField
|
||||
|
||||
from extras.forms import CustomFieldForm, CustomFieldBulkEditForm, CustomFieldFilterForm
|
||||
from ipam.models import IPAddress, VLAN, VLANGroup
|
||||
@ -96,7 +97,7 @@ class SiteForm(BootstrapMixin, TenancyForm, CustomFieldForm):
|
||||
model = Site
|
||||
fields = [
|
||||
'name', 'slug', 'region', 'tenant_group', 'tenant', 'facility', 'asn', 'physical_address',
|
||||
'shipping_address', 'contact_name', 'contact_phone', 'contact_email', 'comments',
|
||||
'shipping_address', 'contact_name', 'contact_phone', 'contact_email', 'time_zone', 'comments',
|
||||
]
|
||||
widgets = {
|
||||
'physical_address': SmallTextarea(attrs={'rows': 3}),
|
||||
@ -135,7 +136,7 @@ class SiteCSVForm(forms.ModelForm):
|
||||
model = Site
|
||||
fields = [
|
||||
'name', 'slug', 'region', 'tenant', 'facility', 'asn', 'physical_address', 'shipping_address',
|
||||
'contact_name', 'contact_phone', 'contact_email', 'comments',
|
||||
'contact_name', 'contact_phone', 'contact_email', 'time_zone', 'comments',
|
||||
]
|
||||
help_texts = {
|
||||
'name': 'Site name',
|
||||
@ -149,9 +150,10 @@ class SiteBulkEditForm(BootstrapMixin, CustomFieldBulkEditForm):
|
||||
region = TreeNodeChoiceField(queryset=Region.objects.all(), required=False)
|
||||
tenant = forms.ModelChoiceField(queryset=Tenant.objects.all(), required=False)
|
||||
asn = forms.IntegerField(min_value=1, max_value=4294967295, required=False, label='ASN')
|
||||
time_zone = TimeZoneFormField(required=False)
|
||||
|
||||
class Meta:
|
||||
nullable_fields = ['region', 'tenant', 'asn']
|
||||
nullable_fields = ['region', 'tenant', 'asn', 'time_zone']
|
||||
|
||||
|
||||
class SiteFilterForm(BootstrapMixin, CustomFieldFilterForm):
|
||||
|
21
netbox/dcim/migrations/0054_site_time_zone.py
Normal file
21
netbox/dcim/migrations/0054_site_time_zone.py
Normal file
@ -0,0 +1,21 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.11.6 on 2017-12-19 21:53
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations
|
||||
import timezone_field.fields
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('dcim', '0053_platform_manufacturer'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='site',
|
||||
name='time_zone',
|
||||
field=timezone_field.fields.TimeZoneField(blank=True),
|
||||
),
|
||||
]
|
@ -14,6 +14,7 @@ from django.db.models import Count, Q, ObjectDoesNotExist
|
||||
from django.urls import reverse
|
||||
from django.utils.encoding import python_2_unicode_compatible
|
||||
from mptt.models import MPTTModel, TreeForeignKey
|
||||
from timezone_field import TimeZoneField
|
||||
|
||||
from circuits.models import Circuit
|
||||
from extras.models import CustomFieldModel, CustomFieldValue, ImageAttachment
|
||||
@ -86,6 +87,7 @@ class Site(CreatedUpdatedModel, CustomFieldModel):
|
||||
tenant = models.ForeignKey(Tenant, related_name='sites', blank=True, null=True, on_delete=models.PROTECT)
|
||||
facility = models.CharField(max_length=50, blank=True)
|
||||
asn = ASNField(blank=True, null=True, verbose_name='ASN')
|
||||
time_zone = TimeZoneField(blank=True)
|
||||
physical_address = models.CharField(max_length=200, blank=True)
|
||||
shipping_address = models.CharField(max_length=200, blank=True)
|
||||
contact_name = models.CharField(max_length=50, blank=True)
|
||||
@ -98,7 +100,8 @@ class Site(CreatedUpdatedModel, CustomFieldModel):
|
||||
objects = SiteManager()
|
||||
|
||||
csv_headers = [
|
||||
'name', 'slug', 'region', 'tenant', 'facility', 'asn', 'contact_name', 'contact_phone', 'contact_email',
|
||||
'name', 'slug', 'region', 'tenant', 'facility', 'asn', 'time_zone', 'contact_name', 'contact_phone',
|
||||
'contact_email',
|
||||
]
|
||||
|
||||
class Meta:
|
||||
@ -118,6 +121,7 @@ class Site(CreatedUpdatedModel, CustomFieldModel):
|
||||
self.tenant.name if self.tenant else None,
|
||||
self.facility,
|
||||
self.asn,
|
||||
self.time_zone,
|
||||
self.contact_name,
|
||||
self.contact_phone,
|
||||
self.contact_email,
|
||||
|
@ -134,6 +134,7 @@ INSTALLED_APPS = (
|
||||
'mptt',
|
||||
'rest_framework',
|
||||
'rest_framework_swagger',
|
||||
'timezone_field',
|
||||
'circuits',
|
||||
'dcim',
|
||||
'ipam',
|
||||
|
@ -1,5 +1,6 @@
|
||||
{% extends '_base.html' %}
|
||||
{% load static from staticfiles %}
|
||||
{% load tz %}
|
||||
{% load helpers %}
|
||||
|
||||
{% block content %}
|
||||
@ -105,6 +106,17 @@
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Time Zone</td>
|
||||
<td>
|
||||
{% if site.time_zone %}
|
||||
{{ site.time_zone }} (UTC {{ site.time_zone|tzoffset }})<br />
|
||||
<small class="text-muted">Site time: {% timezone site.time_zone %}{% now "SHORT_DATETIME_FORMAT" %}{% endtimezone %}</small>
|
||||
{% else %}
|
||||
<span class="text-muted">N/A</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
<div class="panel panel-default">
|
||||
|
@ -10,6 +10,7 @@
|
||||
{% render_field form.region %}
|
||||
{% render_field form.facility %}
|
||||
{% render_field form.asn %}
|
||||
{% render_field form.time_zone %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="panel panel-default">
|
||||
|
@ -1,6 +1,7 @@
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from collections import OrderedDict
|
||||
import pytz
|
||||
|
||||
from django.conf import settings
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
@ -97,6 +98,23 @@ class ContentTypeFieldSerializer(Field):
|
||||
raise ValidationError("Invalid content type")
|
||||
|
||||
|
||||
class TimeZoneField(Field):
|
||||
"""
|
||||
Represent a pytz time zone.
|
||||
"""
|
||||
|
||||
def to_representation(self, obj):
|
||||
return obj.zone if obj else None
|
||||
|
||||
def to_internal_value(self, data):
|
||||
if not data:
|
||||
return ""
|
||||
try:
|
||||
return pytz.timezone(str(data))
|
||||
except pytz.exceptions.UnknownTimeZoneError:
|
||||
raise ValidationError('Invalid time zone "{}"'.format(data))
|
||||
|
||||
|
||||
#
|
||||
# Viewsets
|
||||
#
|
||||
|
@ -1,5 +1,8 @@
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import datetime
|
||||
import pytz
|
||||
|
||||
from django import template
|
||||
from django.utils.safestring import mark_safe
|
||||
from markdown import markdown
|
||||
@ -117,6 +120,14 @@ def example_choices(field, arg=3):
|
||||
return ', '.join(examples) or 'None'
|
||||
|
||||
|
||||
@register.filter()
|
||||
def tzoffset(value):
|
||||
"""
|
||||
Returns the hour offset of a given time zone using the current time.
|
||||
"""
|
||||
return datetime.datetime.now(value).strftime('%z')
|
||||
|
||||
|
||||
#
|
||||
# Tags
|
||||
#
|
||||
|
@ -5,6 +5,7 @@ django-filter>=1.1.0
|
||||
django-mptt==0.8.7
|
||||
django-rest-swagger>=2.1.0
|
||||
django-tables2>=1.10.0
|
||||
django-timezone-field>=2.0
|
||||
djangorestframework>=3.6.4
|
||||
graphviz>=0.6
|
||||
Markdown>=2.6.7
|
||||
|
Loading…
Reference in New Issue
Block a user