mirror of
https://github.com/willmiao/ComfyUI-Lora-Manager.git
synced 2026-06-21 09:52:03 -03:00
feat(models): add group-by-model option to collapse multiple versions into one card
Adds a 'Group by Model' toggle in Layout Settings. When enabled, only the latest version (highest civitai.id) of each Civitai model is shown as a single card — older versions sharing the same modelId are hidden. Backend dedup runs in BaseModelService.get_paginated_data() before filtering/pagination, ensuring correct paginated results. The setting is persisted via the existing settings pipeline and passed as a query parameter to the listing endpoint. Includes: - Backend: dedup logic, route param parsing, settings default - Frontend: API param, SettingsManager wiring, toggle UI - i18n: translations for all 10 locales - Tests: unit test covering dedup on/off and standalone items
This commit is contained in:
@@ -430,6 +430,8 @@
|
|||||||
"help": "Wenn aktiviert, überspringt LoRA Manager den Download einer Modellversion, wenn der Download-Verlaufsdienst diese spezifische Version als bereits heruntergeladen erfasst hat. Gilt für alle Download-Abläufe."
|
"help": "Wenn aktiviert, überspringt LoRA Manager den Download einer Modellversion, wenn der Download-Verlaufsdienst diese spezifische Version als bereits heruntergeladen erfasst hat. Gilt für alle Download-Abläufe."
|
||||||
},
|
},
|
||||||
"layoutSettings": {
|
"layoutSettings": {
|
||||||
|
"groupByModel": "Nach Modell gruppieren",
|
||||||
|
"groupByModelHelp": "Wenn aktiviert, wird nur die neueste Version jedes Civitai-Modells als einzelne Karte angezeigt. Ältere Versionen werden ausgeblendet.",
|
||||||
"displayDensity": "Anzeige-Dichte",
|
"displayDensity": "Anzeige-Dichte",
|
||||||
"displayDensityOptions": {
|
"displayDensityOptions": {
|
||||||
"default": "Standard",
|
"default": "Standard",
|
||||||
|
|||||||
@@ -430,6 +430,8 @@
|
|||||||
"help": "When enabled, versions downloaded before will be skipped."
|
"help": "When enabled, versions downloaded before will be skipped."
|
||||||
},
|
},
|
||||||
"layoutSettings": {
|
"layoutSettings": {
|
||||||
|
"groupByModel": "Group by Model",
|
||||||
|
"groupByModelHelp": "When enabled, only the latest version of each Civitai model is shown as a single card. Older versions are hidden.",
|
||||||
"displayDensity": "Display Density",
|
"displayDensity": "Display Density",
|
||||||
"displayDensityOptions": {
|
"displayDensityOptions": {
|
||||||
"default": "Default",
|
"default": "Default",
|
||||||
|
|||||||
@@ -430,6 +430,8 @@
|
|||||||
"help": "Cuando está habilitado, LoRA Manager omitirá la descarga de una versión de modelo si el servicio de historial de descargas registra esa versión exacta como ya descargada. Aplica a todos los flujos de descarga."
|
"help": "Cuando está habilitado, LoRA Manager omitirá la descarga de una versión de modelo si el servicio de historial de descargas registra esa versión exacta como ya descargada. Aplica a todos los flujos de descarga."
|
||||||
},
|
},
|
||||||
"layoutSettings": {
|
"layoutSettings": {
|
||||||
|
"groupByModel": "Agrupar por modelo",
|
||||||
|
"groupByModelHelp": "Cuando está activado, solo se muestra la versión más reciente de cada modelo de Civitai como una tarjeta única. Las versiones anteriores están ocultas.",
|
||||||
"displayDensity": "Densidad de visualización",
|
"displayDensity": "Densidad de visualización",
|
||||||
"displayDensityOptions": {
|
"displayDensityOptions": {
|
||||||
"default": "Predeterminado",
|
"default": "Predeterminado",
|
||||||
|
|||||||
@@ -430,6 +430,8 @@
|
|||||||
"help": "Lorsque activé, LoRA Manager ignorera le téléchargement d'une version de modèle si le service d'historique des téléchargements enregistre cette version exacte comme déjà téléchargée. S'applique à tous les flux de téléchargement."
|
"help": "Lorsque activé, LoRA Manager ignorera le téléchargement d'une version de modèle si le service d'historique des téléchargements enregistre cette version exacte comme déjà téléchargée. S'applique à tous les flux de téléchargement."
|
||||||
},
|
},
|
||||||
"layoutSettings": {
|
"layoutSettings": {
|
||||||
|
"groupByModel": "Grouper par modèle",
|
||||||
|
"groupByModelHelp": "Lorsque activé, seule la version la plus récente de chaque modèle Civitai s'affiche sous forme de carte unique. Les versions plus anciennes sont masquées.",
|
||||||
"displayDensity": "Densité d'affichage",
|
"displayDensity": "Densité d'affichage",
|
||||||
"displayDensityOptions": {
|
"displayDensityOptions": {
|
||||||
"default": "Par défaut",
|
"default": "Par défaut",
|
||||||
|
|||||||
@@ -430,6 +430,8 @@
|
|||||||
"help": "כאשר מופעל, LoRA Manager ידלג על הורדת גרסת מודל אם שירות היסטוריית ההורדות רושם את הגרסה המדויקת הזו ככבר שהורדה. חל על כל תהליכי ההורדה."
|
"help": "כאשר מופעל, LoRA Manager ידלג על הורדת גרסת מודל אם שירות היסטוריית ההורדות רושם את הגרסה המדויקת הזו ככבר שהורדה. חל על כל תהליכי ההורדה."
|
||||||
},
|
},
|
||||||
"layoutSettings": {
|
"layoutSettings": {
|
||||||
|
"groupByModel": "קיבוץ לפי דגם",
|
||||||
|
"groupByModelHelp": "כאשר מופעל, רק הגרסה העדכנית ביותר של כל דגם Civitai מוצגת ככרטיס בודד. גרסאות ישנות יותר מוסתרות.",
|
||||||
"displayDensity": "צפיפות תצוגה",
|
"displayDensity": "צפיפות תצוגה",
|
||||||
"displayDensityOptions": {
|
"displayDensityOptions": {
|
||||||
"default": "ברירת מחדל",
|
"default": "ברירת מחדל",
|
||||||
|
|||||||
@@ -430,6 +430,8 @@
|
|||||||
"help": "有効にすると、ダウンロード履歴サービスがそのバージョンが既にダウンロード済みと記録している場合、LoRA Managerはそのモデルバージョンのダウンロードをスキップします。すべてのダウンロードフローに適用されます。"
|
"help": "有効にすると、ダウンロード履歴サービスがそのバージョンが既にダウンロード済みと記録している場合、LoRA Managerはそのモデルバージョンのダウンロードをスキップします。すべてのダウンロードフローに適用されます。"
|
||||||
},
|
},
|
||||||
"layoutSettings": {
|
"layoutSettings": {
|
||||||
|
"groupByModel": "モデルでグループ化",
|
||||||
|
"groupByModelHelp": "有効にすると、各Civitaiモデルの最新バージョンのみが1枚のカードとして表示され、古いバージョンは非表示になります。",
|
||||||
"displayDensity": "表示密度",
|
"displayDensity": "表示密度",
|
||||||
"displayDensityOptions": {
|
"displayDensityOptions": {
|
||||||
"default": "デフォルト",
|
"default": "デフォルト",
|
||||||
|
|||||||
@@ -430,6 +430,8 @@
|
|||||||
"help": "활성화하면 다운로드 기록 서비스가 해당 버전이 이미 다운로드되었음을 기록한 경우 LoRA Manager는 해당 모델 버전 다운로드를 건너뜁니다. 모든 다운로드 플로우에 적용됩니다."
|
"help": "활성화하면 다운로드 기록 서비스가 해당 버전이 이미 다운로드되었음을 기록한 경우 LoRA Manager는 해당 모델 버전 다운로드를 건너뜁니다. 모든 다운로드 플로우에 적용됩니다."
|
||||||
},
|
},
|
||||||
"layoutSettings": {
|
"layoutSettings": {
|
||||||
|
"groupByModel": "모델별 그룹화",
|
||||||
|
"groupByModelHelp": "활성화하면 각 Civitai 모델의 최신 버전만 단일 카드로 표시되며, 이전 버전은 숨겨집니다.",
|
||||||
"displayDensity": "표시 밀도",
|
"displayDensity": "표시 밀도",
|
||||||
"displayDensityOptions": {
|
"displayDensityOptions": {
|
||||||
"default": "기본",
|
"default": "기본",
|
||||||
|
|||||||
@@ -430,6 +430,8 @@
|
|||||||
"help": "Если включено, LoRA Manager будет пропускать загрузку версии модели, если сервис истории загрузок записал, что эта конкретная версия уже загружена. Применяется ко всем потокам загрузки."
|
"help": "Если включено, LoRA Manager будет пропускать загрузку версии модели, если сервис истории загрузок записал, что эта конкретная версия уже загружена. Применяется ко всем потокам загрузки."
|
||||||
},
|
},
|
||||||
"layoutSettings": {
|
"layoutSettings": {
|
||||||
|
"groupByModel": "Группировать по модели",
|
||||||
|
"groupByModelHelp": "При включении отображается только последняя версия каждой модели Civitai в виде одной карточки. Старые версии скрыты.",
|
||||||
"displayDensity": "Плотность отображения",
|
"displayDensity": "Плотность отображения",
|
||||||
"displayDensityOptions": {
|
"displayDensityOptions": {
|
||||||
"default": "По умолчанию",
|
"default": "По умолчанию",
|
||||||
|
|||||||
@@ -430,6 +430,8 @@
|
|||||||
"help": "启用后,如果下载历史服务记录显示该版本已下载,LoRA Manager 将跳过下载该模型版本。适用于所有下载流程。"
|
"help": "启用后,如果下载历史服务记录显示该版本已下载,LoRA Manager 将跳过下载该模型版本。适用于所有下载流程。"
|
||||||
},
|
},
|
||||||
"layoutSettings": {
|
"layoutSettings": {
|
||||||
|
"groupByModel": "按模型分组",
|
||||||
|
"groupByModelHelp": "开启后,每个 Civitai 模型仅显示最新版本的单张卡片,旧版本将被隐藏。",
|
||||||
"displayDensity": "显示密度",
|
"displayDensity": "显示密度",
|
||||||
"displayDensityOptions": {
|
"displayDensityOptions": {
|
||||||
"default": "默认",
|
"default": "默认",
|
||||||
|
|||||||
@@ -430,6 +430,8 @@
|
|||||||
"help": "啟用後,如果下載歷史服務記錄顯示該版本已下載,LoRA Manager 將跳過下載該模型版本。適用於所有下載流程。"
|
"help": "啟用後,如果下載歷史服務記錄顯示該版本已下載,LoRA Manager 將跳過下載該模型版本。適用於所有下載流程。"
|
||||||
},
|
},
|
||||||
"layoutSettings": {
|
"layoutSettings": {
|
||||||
|
"groupByModel": "按模型分組",
|
||||||
|
"groupByModelHelp": "啟用後,每個 Civitai 模型僅顯示最新版本的單張卡片,舊版本將被隱藏。",
|
||||||
"displayDensity": "顯示密度",
|
"displayDensity": "顯示密度",
|
||||||
"displayDensityOptions": {
|
"displayDensityOptions": {
|
||||||
"default": "預設",
|
"default": "預設",
|
||||||
|
|||||||
@@ -233,6 +233,8 @@ class ModelListingHandler:
|
|||||||
start_time = time.perf_counter()
|
start_time = time.perf_counter()
|
||||||
try:
|
try:
|
||||||
params = self._parse_common_params(request)
|
params = self._parse_common_params(request)
|
||||||
|
# group_by_model is meaningless for excluded view; strip it
|
||||||
|
params.pop("group_by_model", None)
|
||||||
result = await self._service.get_excluded_paginated_data(**params)
|
result = await self._service.get_excluded_paginated_data(**params)
|
||||||
|
|
||||||
format_start = time.perf_counter()
|
format_start = time.perf_counter()
|
||||||
@@ -366,6 +368,11 @@ class ModelListingHandler:
|
|||||||
request.query.get("name_pattern_use_regex", "false").lower() == "true"
|
request.query.get("name_pattern_use_regex", "false").lower() == "true"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Group-by-model flag: deduplicate versions sharing the same civitai modelId
|
||||||
|
group_by_model = (
|
||||||
|
request.query.get("group_by_model", "false").lower() == "true"
|
||||||
|
)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"page": page,
|
"page": page,
|
||||||
"page_size": page_size,
|
"page_size": page_size,
|
||||||
@@ -389,6 +396,7 @@ class ModelListingHandler:
|
|||||||
"name_pattern_include": name_pattern_include,
|
"name_pattern_include": name_pattern_include,
|
||||||
"name_pattern_exclude": name_pattern_exclude,
|
"name_pattern_exclude": name_pattern_exclude,
|
||||||
"name_pattern_use_regex": name_pattern_use_regex,
|
"name_pattern_use_regex": name_pattern_use_regex,
|
||||||
|
"group_by_model": group_by_model,
|
||||||
**self._parse_specific_params(request),
|
**self._parse_specific_params(request),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -104,6 +104,22 @@ class BaseModelService(ABC):
|
|||||||
fetch_duration = time.perf_counter() - t0
|
fetch_duration = time.perf_counter() - t0
|
||||||
initial_count = len(sorted_data)
|
initial_count = len(sorted_data)
|
||||||
|
|
||||||
|
# Optionally group by civitai modelId, showing only the latest version per model
|
||||||
|
dedup_lost = 0
|
||||||
|
if kwargs.get("group_by_model"):
|
||||||
|
dedup_map = {} # modelId -> (item, version_id)
|
||||||
|
standalone = []
|
||||||
|
for item in sorted_data:
|
||||||
|
mid = self._extract_model_id(item)
|
||||||
|
if mid is None:
|
||||||
|
standalone.append(item)
|
||||||
|
continue
|
||||||
|
vid = self._extract_version_id(item) or 0
|
||||||
|
if mid not in dedup_map or vid > dedup_map[mid][1]:
|
||||||
|
dedup_map[mid] = (item, vid)
|
||||||
|
dedup_lost = len(sorted_data) - (len(dedup_map) + len(standalone))
|
||||||
|
sorted_data = [entry[0] for entry in dedup_map.values()] + standalone
|
||||||
|
|
||||||
t1 = time.perf_counter()
|
t1 = time.perf_counter()
|
||||||
if hash_filters:
|
if hash_filters:
|
||||||
filtered_data = await self._apply_hash_filters(sorted_data, hash_filters)
|
filtered_data = await self._apply_hash_filters(sorted_data, hash_filters)
|
||||||
@@ -172,7 +188,7 @@ class BaseModelService(ABC):
|
|||||||
overall_duration = time.perf_counter() - overall_start
|
overall_duration = time.perf_counter() - overall_start
|
||||||
logger.debug(
|
logger.debug(
|
||||||
"%s.get_paginated_data took %.3fs (fetch: %.3fs, filter: %.3fs, update_filter: %.3fs, pagination: %.3fs, annotate: %.3fs). "
|
"%s.get_paginated_data took %.3fs (fetch: %.3fs, filter: %.3fs, update_filter: %.3fs, pagination: %.3fs, annotate: %.3fs). "
|
||||||
"Counts: initial=%d, post_filter=%d, final=%d",
|
"Counts: initial=%d, dedup=%d, post_filter=%d, final=%d",
|
||||||
self.__class__.__name__,
|
self.__class__.__name__,
|
||||||
overall_duration,
|
overall_duration,
|
||||||
fetch_duration,
|
fetch_duration,
|
||||||
@@ -181,6 +197,7 @@ class BaseModelService(ABC):
|
|||||||
pagination_duration,
|
pagination_duration,
|
||||||
annotate_duration,
|
annotate_duration,
|
||||||
initial_count,
|
initial_count,
|
||||||
|
dedup_lost,
|
||||||
post_filter_count,
|
post_filter_count,
|
||||||
final_count,
|
final_count,
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -106,6 +106,7 @@ DEFAULT_SETTINGS: Dict[str, Any] = {
|
|||||||
"backup_auto_enabled": True,
|
"backup_auto_enabled": True,
|
||||||
"backup_retention_count": 5,
|
"backup_retention_count": 5,
|
||||||
"use_new_license_icons": True,
|
"use_new_license_icons": True,
|
||||||
|
"group_by_model": False,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1271,6 +1271,11 @@ export class BaseModelApiClient {
|
|||||||
|
|
||||||
params.append('recursive', pageState.searchOptions.recursive ? 'true' : 'false');
|
params.append('recursive', pageState.searchOptions.recursive ? 'true' : 'false');
|
||||||
|
|
||||||
|
// Pass group-by-model mode to backend
|
||||||
|
if (state.global.settings.group_by_model) {
|
||||||
|
params.append('group_by_model', 'true');
|
||||||
|
}
|
||||||
|
|
||||||
if (!isExcludedView && pageState.filters) {
|
if (!isExcludedView && pageState.filters) {
|
||||||
if (pageState.filters.tags && Object.keys(pageState.filters.tags).length > 0) {
|
if (pageState.filters.tags && Object.keys(pageState.filters.tags).length > 0) {
|
||||||
Object.entries(pageState.filters.tags).forEach(([tag, state]) => {
|
Object.entries(pageState.filters.tags).forEach(([tag, state]) => {
|
||||||
|
|||||||
@@ -905,6 +905,12 @@ export class SettingsManager {
|
|||||||
showVersionOnCardCheckbox.checked = state.global.settings.show_version_on_card !== false;
|
showVersionOnCardCheckbox.checked = state.global.settings.show_version_on_card !== false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Set group by model
|
||||||
|
const groupByModelCheckbox = document.getElementById('groupByModel');
|
||||||
|
if (groupByModelCheckbox) {
|
||||||
|
groupByModelCheckbox.checked = !!state.global.settings.group_by_model;
|
||||||
|
}
|
||||||
|
|
||||||
// Set model name display setting
|
// Set model name display setting
|
||||||
const modelNameDisplaySelect = document.getElementById('modelNameDisplay');
|
const modelNameDisplaySelect = document.getElementById('modelNameDisplay');
|
||||||
if (modelNameDisplaySelect) {
|
if (modelNameDisplaySelect) {
|
||||||
@@ -2011,7 +2017,7 @@ export class SettingsManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (settingKey === 'show_only_sfw' || settingKey === 'blur_mature_content') {
|
if (settingKey === 'show_only_sfw' || settingKey === 'blur_mature_content' || settingKey === 'group_by_model') {
|
||||||
this.reloadContent();
|
this.reloadContent();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3046,6 +3052,10 @@ export class SettingsManager {
|
|||||||
const useNewLicenseIcons = state.global.settings.use_new_license_icons !== false;
|
const useNewLicenseIcons = state.global.settings.use_new_license_icons !== false;
|
||||||
document.body.classList.toggle('use-new-license-icons', useNewLicenseIcons);
|
document.body.classList.toggle('use-new-license-icons', useNewLicenseIcons);
|
||||||
|
|
||||||
|
// Apply group-by-model mode
|
||||||
|
const groupByModel = !!state.global.settings.group_by_model;
|
||||||
|
document.body.classList.toggle('group-by-model', groupByModel);
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -54,6 +54,7 @@ const DEFAULT_SETTINGS_BASE = Object.freeze({
|
|||||||
backup_retention_count: 5,
|
backup_retention_count: 5,
|
||||||
strip_lora_on_copy: false,
|
strip_lora_on_copy: false,
|
||||||
use_new_license_icons: true,
|
use_new_license_icons: true,
|
||||||
|
group_by_model: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
export function createDefaultSettings() {
|
export function createDefaultSettings() {
|
||||||
|
|||||||
@@ -536,6 +536,25 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Group by model toggle -->
|
||||||
|
<div class="setting-item">
|
||||||
|
<div class="setting-row">
|
||||||
|
<div class="setting-info">
|
||||||
|
<label for="groupByModel">
|
||||||
|
{{ t('settings.layoutSettings.groupByModel') }}
|
||||||
|
<i class="fas fa-info-circle info-icon" data-tooltip="{{ t('settings.layoutSettings.groupByModelHelp') }}"></i>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class="setting-control">
|
||||||
|
<label class="toggle-switch">
|
||||||
|
<input type="checkbox" id="groupByModel"
|
||||||
|
onchange="settingsManager.saveToggleSetting('groupByModel', 'group_by_model')">
|
||||||
|
<span class="toggle-slider"></span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="setting-item">
|
<div class="setting-item">
|
||||||
<div class="setting-row">
|
<div class="setting-row">
|
||||||
<div class="setting-info">
|
<div class="setting-info">
|
||||||
|
|||||||
@@ -746,6 +746,63 @@ async def test_get_paginated_data_update_available_only_without_update_service()
|
|||||||
assert response["total_pages"] == 0
|
assert response["total_pages"] == 0
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_get_paginated_data_group_by_model_dedup():
|
||||||
|
"""group_by_model deduplicates items sharing the same civitai modelId,
|
||||||
|
keeping only the item with the highest version (civitai.id)."""
|
||||||
|
items = [
|
||||||
|
# Two versions of the same model (modelId=1)
|
||||||
|
{"model_name": "SameModel", "folder": "root", "civitai": {"modelId": 1, "id": 100}},
|
||||||
|
{"model_name": "SameModel", "folder": "root", "civitai": {"modelId": 1, "id": 200}},
|
||||||
|
# Another model with two versions
|
||||||
|
{"model_name": "AnotherModel", "folder": "root", "civitai": {"modelId": 2, "id": 50}},
|
||||||
|
{"model_name": "AnotherModel", "folder": "root", "civitai": {"modelId": 2, "id": 99}},
|
||||||
|
# A standalone item with no civitai metadata (no modelId)
|
||||||
|
{"model_name": "Standalone", "folder": "root"},
|
||||||
|
]
|
||||||
|
repository = StubRepository(items)
|
||||||
|
filter_set = PassThroughFilterSet()
|
||||||
|
search_strategy = NoSearchStrategy()
|
||||||
|
settings = StubSettings({})
|
||||||
|
|
||||||
|
service = DummyService(
|
||||||
|
model_type="stub",
|
||||||
|
scanner=object(),
|
||||||
|
metadata_class=BaseModelMetadata,
|
||||||
|
cache_repository=repository,
|
||||||
|
filter_set=filter_set,
|
||||||
|
search_strategy=search_strategy,
|
||||||
|
settings_provider=settings,
|
||||||
|
)
|
||||||
|
|
||||||
|
# With group_by_model=True — modelId=1 keeps id=200, modelId=2 keeps id=99
|
||||||
|
response = await service.get_paginated_data(
|
||||||
|
page=1,
|
||||||
|
page_size=10,
|
||||||
|
sort_by="name:asc",
|
||||||
|
group_by_model=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
names = {item["model_name"] for item in response["items"]}
|
||||||
|
assert names == {"SameModel", "AnotherModel", "Standalone"}
|
||||||
|
assert response["total"] == 3
|
||||||
|
# Verify the kept items have the highest version id
|
||||||
|
for item in response["items"]:
|
||||||
|
if item.get("civitai", {}).get("modelId") == 1:
|
||||||
|
assert item["civitai"]["id"] == 200
|
||||||
|
elif item.get("civitai", {}).get("modelId") == 2:
|
||||||
|
assert item["civitai"]["id"] == 99
|
||||||
|
|
||||||
|
# With group_by_model=False (default) — all 5 items pass through
|
||||||
|
response_all = await service.get_paginated_data(
|
||||||
|
page=1,
|
||||||
|
page_size=10,
|
||||||
|
sort_by="name:asc",
|
||||||
|
)
|
||||||
|
|
||||||
|
assert response_all["total"] == 5
|
||||||
|
|
||||||
|
|
||||||
def test_model_filter_set_handles_include_and_exclude_tag_filters():
|
def test_model_filter_set_handles_include_and_exclude_tag_filters():
|
||||||
settings = StubSettings({})
|
settings = StubSettings({})
|
||||||
filter_set = ModelFilterSet(settings)
|
filter_set = ModelFilterSet(settings)
|
||||||
|
|||||||
Reference in New Issue
Block a user