Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Test on Django 5.0 #3269

Open
wants to merge 9 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/build-and-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ jobs:
strategy:
max-parallel: 4
matrix:
python-version: ["3.9", "3.10"]
python-version: ["3.9", "3.10", "3.11", "3.12"]

steps:
- uses: actions/checkout@v4
Expand Down
2 changes: 1 addition & 1 deletion python/nav/ipdevpoll/shadows/interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,14 @@
from nav.models import manage
from nav.models.event import EventQueue as Event, EventQueueVar as EventVar
from nav.models.event import AlertHistory
from nav.models.fields import INFINITY
from nav import natsort

from nav.ipdevpoll.storage import Shadow, DefaultManager

from .netbox import Netbox

MISSING_THRESHOLD = datetime.timedelta(days=1)
INFINITY = datetime.datetime.max

# pylint: disable=C0111

Expand Down
2 changes: 0 additions & 2 deletions python/nav/maintengine.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,6 @@
from nav.models.event import EventQueue as Event, AlertHistory
from nav.models.msgmaint import MaintenanceTask

INFINITY = datetime.datetime.max

# The devices must have been up for at least this time before
# ending a maintenance task without a specified end.
MINIMUM_UPTIME = datetime.timedelta(minutes=60)
Expand Down
56 changes: 31 additions & 25 deletions python/nav/models/fields.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
from django import forms
from django.db import models
from django.db.models import signals
from django.db.models.fields.mixins import FieldCacheMixin
from django.core import exceptions
from django.db.models import Q
from django.apps import apps
Expand Down Expand Up @@ -146,7 +147,7 @@ def formfield(self, **kwargs):
# this interfaces with Django model protocols, which generates unnecessary
# pylint violations:
# pylint: disable=W0201,W0212
class LegacyGenericForeignKey(object):
class LegacyGenericForeignKey(FieldCacheMixin):
"""Generic foreign key for legacy NAV database.

Some legacy tables in NAV have generic foreign keys that look very much
Expand All @@ -155,16 +156,25 @@ class LegacyGenericForeignKey(object):

"""

# Field flags
auto_created = False
concrete = False
editable = False
hidden = False

is_relation = True
many_to_many = False
many_to_one = True
one_to_one = False
related_model = None
remote_field = None

def __init__(self, model_name_field, model_fk_field, for_concrete_model=True):
self.mn_field = model_name_field
self.fk_field = model_fk_field
self.is_relation = True
self.many_to_many = False
self.one_to_many = True
self.related_model = None
self.auto_created = False
self.for_concrete_model = for_concrete_model
self.editable = False
self.for_concrete_model = for_concrete_model

def __str__(self):
modelname = getattr(self, 'mn_field')
Expand All @@ -175,14 +185,16 @@ def contribute_to_class(self, cls, name):
"""Add things to the model class using this descriptor"""
self.name = name
self.model = cls
self.cache_attr = "_%s_cache" % name
cls._meta.private_fields.append(self)

if not cls._meta.abstract:
signals.pre_init.connect(self.instance_pre_init, sender=cls)

setattr(cls, name, self)

def get_cache_name(self):
return self.name

def instance_pre_init(self, signal, sender, args, kwargs, **_kwargs):
"""
Handles initializing an object with the generic FK instead of
Expand All @@ -197,28 +209,22 @@ def instance_pre_init(self, signal, sender, args, kwargs, **_kwargs):
kwargs[self.mn_field] = None
kwargs[self.fk_field] = None

def is_cached(self, instance):
return hasattr(instance, self.cache_attr)

def __get__(self, instance, instance_type=None):
if instance is None:
return self

try:
return getattr(instance, self.cache_attr)
except AttributeError:
rel_obj = None
rel_obj = self.get_cached_value(instance, default=None)

field = self.model._meta.get_field(self.mn_field)
table_name = getattr(instance, field.get_attname(), None)
rel_model = self.get_model_class(table_name)
if rel_model:
try:
rel_obj = rel_model.objects.get(id=getattr(instance, self.fk_field))
except exceptions.ObjectDoesNotExist:
pass
setattr(instance, self.cache_attr, rel_obj)
return rel_obj
field = self.model._meta.get_field(self.mn_field)
table_name = getattr(instance, field.get_attname(), None)
rel_model = self.get_model_class(table_name)
if rel_model:
try:
rel_obj = rel_model.objects.get(id=getattr(instance, self.fk_field))
except exceptions.ObjectDoesNotExist:
pass
self.set_cached_value(instance, rel_obj)
return rel_obj

def __set__(self, instance, value):
if instance is None:
Expand All @@ -232,7 +238,7 @@ def __set__(self, instance, value):

setattr(instance, self.mn_field, table_name)
setattr(instance, self.fk_field, fkey)
setattr(instance, self.cache_attr, value)
self.set_cached_value(instance, value)

@staticmethod
def get_model_name(obj) -> str:
Expand Down
3 changes: 2 additions & 1 deletion python/nav/web/auth/middleware.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
authorization_not_required,
)
from nav.web.auth.sudo import get_sudoer
from nav.web.utils import is_ajax


_logger = logging.getLogger(__name__)
Expand Down Expand Up @@ -107,7 +108,7 @@ def redirect_to_login(self, request):
response.

"""
if request.is_ajax():
if is_ajax(request):
return HttpResponse(status=401)

new_url = get_login_url(request)
Expand Down
5 changes: 2 additions & 3 deletions python/nav/web/l2trace/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,15 @@
#
"""Layer 2 traceroute web tool for NAV."""

import datetime
import socket
from socket import gethostbyaddr, gethostbyname

from django.db.models import Q
from nav.models.manage import Netbox, SwPortVlan, GwPortPrefix, Prefix, Arp, Cam

from nav.models.fields import INFINITY
from nav.models.manage import Netbox, SwPortVlan, GwPortPrefix, Prefix, Arp, Cam
from nav.util import is_valid_ip

INFINITY = datetime.datetime.max
PATH_NOT_FOUND = None
LAYER_3_PATH = -1

Expand Down
4 changes: 1 addition & 3 deletions python/nav/web/maintenance/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
from django.urls import reverse
from django.utils.html import conditional_escape

from nav.models.fields import LegacyGenericForeignKey
from nav.models.fields import LegacyGenericForeignKey, INFINITY
from nav.models.manage import Netbox, Room, Location, NetboxGroup
from nav.models.service import Service
from nav.models.msgmaint import MaintenanceTask, MaintenanceComponent
Expand All @@ -48,8 +48,6 @@

TITLE = "NAV - Maintenance"

INFINITY = datetime.max


def task_form_initial(task=None, start_time=None):
if task:
Expand Down
11 changes: 4 additions & 7 deletions python/nav/web/maintenance/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,11 @@
from django.shortcuts import get_object_or_404, redirect, render
from django.urls import reverse
from django.utils.safestring import mark_safe
from django.utils.timezone import now as utcnow

import nav.maintengine
from nav.django.utils import get_account
from nav.models.fields import INFINITY
from nav.models.manage import Netbox
from nav.models.msgmaint import MaintenanceComponent, MaintenanceTask
from nav.web.maintenance.forms import (
Expand All @@ -50,8 +52,6 @@
from nav.web.message import Messages, new_message
from nav.web.quickselect import QuickSelect

INFINITY = datetime.max

_logger = logging.getLogger(__name__)


Expand Down Expand Up @@ -278,11 +278,8 @@ def edit(request, task_id=None, start_time=None, **_):
end_time = task_form.cleaned_data['end_time']
no_end_time = task_form.cleaned_data['no_end_time']
state = MaintenanceTask.STATE_SCHEDULED
if (
start_time < datetime.now()
and end_time
and end_time <= datetime.now()
):
now = utcnow()
if start_time < now and end_time and end_time <= now:
state = MaintenanceTask.STATE_SCHEDULED

new_task = MaintenanceTask()
Expand Down
8 changes: 4 additions & 4 deletions python/nav/web/syslogger/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
from nav.models.logger import LogMessage
from nav.models.logger import ErrorError
from nav.web.syslogger.forms import LoggerGroupSearchForm
from nav.web.utils import create_title
from nav.web.utils import create_title, is_ajax


DATEFORMAT = "%Y-%m-%d %H:%M:%S"
Expand Down Expand Up @@ -204,7 +204,7 @@ def index(request):


def group_search(request):
if not request.is_ajax():
if not is_ajax(request):
return HttpResponseRedirect(reverse(index) + '?' + request.GET.urlencode())
return handle_search(request, LoggerGroupSearchForm, reverse(group_search))

Expand All @@ -213,7 +213,7 @@ def exceptions_response(request):
"""
Handler for exception-mode.
"""
if not request.is_ajax():
if not is_ajax(request):
return HttpResponseRedirect(reverse(index) + '?' + request.GET.urlencode())

account = get_account(request)
Expand All @@ -236,7 +236,7 @@ def errors_response(request):
"""
Handler for error-mode.
"""
if not request.is_ajax():
if not is_ajax(request):
return HttpResponseRedirect(reverse(index) + '?' + request.GET.urlencode())

account = get_account(request)
Expand Down
4 changes: 4 additions & 0 deletions python/nav/web/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@
import qrcode.image.pil


def is_ajax(request):
return request.headers.get("x-requested-with") == "XMLHttpRequest"


def get_navpath_root():
"""Returns the default navpath root

Expand Down
2 changes: 1 addition & 1 deletion requirements/base.txt
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ feedparser==6.0.8
dnspython<3.0.0,>=2.1.0

django-filter>=2
djangorestframework>=3.12,<3.13
djangorestframework>=3.12

# REST framework
iso8601
Expand Down
1 change: 1 addition & 0 deletions requirements/django32.txt
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
Django>=3.2,<3.3
djangorestframework>=3.12,<3.13
2 changes: 2 additions & 0 deletions requirements/django42.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Django>=4.2,<4.3
pytz
1 change: 1 addition & 0 deletions requirements/django50.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Django>=5.0,<5.1
2 changes: 2 additions & 0 deletions tests/docker/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ RUN add-apt-repository ppa:deadsnakes/ppa && \
curl git build-essential \
python3.9-dbg python3.9-dev \
python3.10-dbg python3.10-dev \
python3.11-dbg python3.11-dev \
python3.12-dbg python3.12-dev \
python3-pip

RUN echo "deb http://dl.google.com/linux/chrome/deb/ stable main" > /etc/apt/sources.list.d/google-chrome.list
Expand Down
7 changes: 6 additions & 1 deletion tox.ini
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
[tox]
envlist =
{unit,integration,functional}-py{39,310}-django{32}
{unit,integration,functional}-py{39,310,311}-django{42}
{unit,integration,functional}-py{310,311}-django{50}
javascript
docs
basepython = python3.9
Expand All @@ -21,6 +23,8 @@ markers =
python =
3.9: py39
3.10: py310
3.11: py311
3.12: py312

[testenv]
# Baseline test environment
Expand All @@ -43,7 +47,8 @@ setenv =
COVERAGE_FILE = {toxinidir}/reports/coverage/.coverage
PYTHONFAULTHANDLER=1
django32: DJANGO_VER=32
django40: DJANGO_VER=40
django42: DJANGO_VER=42
django50: DJANGO_VER=50
passenv =
C_INCLUDE_PATH
GITHUB_ACTIONS
Expand Down
Loading